1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 
5  Author: Jonathan M. Henson
6  Date: 07/08/2012
7  Contact: jonathan.michael.henson@gmail.com
8  */
9 
10 #define LOCAL_MEDIA
11 
12 using System;
13 using System.Collections.Generic;
14 using System.Linq;
15 using System.Text;
16 using OpalDotNET;
17 using System.IO;
18 using System.Runtime.InteropServices;
19 
20 namespace OpalDotNETMain
21 {
22   class Program
23   {
24     static string currentCallToken;
25     static string heldCallToken;
26     static string playScript;
27     static OpalContext context = new OpalContext();
28     static FileStream audioInputFile = null;
29     static FileStream audioOutputFile = null;
30 
31 
MySendCommand(OpalMessageRef command, string errorMessage)32     static OpalMessageRef MySendCommand(OpalMessageRef command, string errorMessage)
33     {
34       OpalMessageRef response = new OpalMessageRef();
35       if(!context.SendMessage(command, response))
36           return null;
37 
38       if (response.GetMessageType() != Opal_API.OpalMessageType.OpalIndCommandError)
39         return response;
40 
41       if (response.GetCommandError() == null || response.GetCommandError() == "\0")
42         Console.WriteLine("{0}.\n", errorMessage);
43       else
44         Console.WriteLine("{0}: {1}\n", errorMessage, response.GetCommandError());
45 
46       return null;
47     }
48 
49 #if LOCAL_MEDIA
MyReadMediaData(string token, string id, string format, IntPtr userData, IntPtr data, int size)50     static int MyReadMediaData(string token, string id, string format, IntPtr userData, IntPtr data, int size)
51     {
52       if (audioInputFile == null)
53       {
54         if (format == "PCM-16")
55           audioInputFile = File.Open("ogm.wav", FileMode.Open);
56         Console.WriteLine("Reading {0} media for stream {1} on call {2}{3}", format, id, token, Environment.NewLine);
57       }
58 
59       if (audioInputFile != null)
60       {
61         byte[] byData = new byte[size];
62         int written = audioInputFile.Read(byData, 0, size);
63         Marshal.Copy(byData, 0, data, written);
64         return written;
65       }
66 
67       return size;
68     }
69 
70 
MyWriteMediaData(string token, string id, string format, IntPtr userData, IntPtr data, int size)71     static int MyWriteMediaData(string token, string id, string format, IntPtr userData, IntPtr data, int size)
72     {
73       if (audioOutputFile == null)
74       {
75         string name;
76         name = String.Format("Media-{0}-{1}.{2}", token, id, format);
77         audioOutputFile = File.OpenWrite(name);
78 
79         if (audioOutputFile == null)
80         {
81           Console.WriteLine("Could not create media output file \"{0}\"{1}", name, Environment.NewLine);
82           return -1;
83         }
84 
85         Console.WriteLine("Writing {0} media for stream {1} on call {2}{3}", format, id, token, Environment.NewLine);
86       }
87 
88       byte[] byData = new byte[size];
89       Marshal.Copy(data, byData, 0, size);
90 
91       audioOutputFile.Write(byData, 0, size);
92 
93       return size;
94     }
95 #endif
96 
InitialiseOPAL()97     static int InitialiseOPAL()
98     {
99       OpalMessageRef command;
100       OpalMessageRef response;
101       uint version;
102 
103       string OPALOptions = Opal_API.OPAL_PREFIX_H323  + " " +
104                                     Opal_API.OPAL_PREFIX_SIP + " " +
105                                     Opal_API.OPAL_PREFIX_IAX2 + " " +
106 #if LOCAL_MEDIA
107                                     Opal_API.OPAL_PREFIX_LOCAL +
108 #else
109                                     Opal_API.OPAL_PREFIX_PCSS +
110 #endif
111                                     " " +
112                                     Opal_API.OPAL_PREFIX_IVR +
113                                     " TraceLevel=4 TraceFile=debugstream";
114 
115         ///////////////////////////////////////////////
116         // Initialisation
117 
118         version = Opal_API.OPAL_C_API_VERSION;
119 
120         if (context.Initialise(OPALOptions, version) == version)
121         {
122           Console.WriteLine("Could not initialise OPAL{0}", Environment.NewLine);
123           return 0;
124         }
125 
126         #if NULL
127         // Test shut down and re-initialisation
128         context.ShutDown();
129 
130         if (context.Initialise(OPALOptions, version) == version)
131         {
132           Console.WriteLine("Could not reinitialise OPAL{0}", Environment.NewLine);
133           return 0;
134         }
135         #endif
136 
137         // General options
138         command = new OpalMessageRef(Opal_API.OpalMessageType.OpalCmdSetGeneralParameters);
139         //command.m_param.m_general.m_audioRecordDevice = "Camera Microphone (2- Logitech";
140         OpalParamGeneralRef m_general = command.GetGeneralParams();
141 
142         m_general.AutoRxMedia = m_general.AutoTxMedia = "audio";
143         m_general.StunServer = "stun.voxgratia.org";
144         m_general.MediaMask = "RFC4175*";
145 
146 #if LOCAL_MEDIA
147         m_general.MediaReadData = MyReadMediaData;
148         m_general.MediaWriteData = MyWriteMediaData;
149         m_general.MediaDataHeader = (uint)Opal_API.OpalMediaDataType.OpalMediaDataPayloadOnly;
150 #endif
151 
152         if ((response = MySendCommand(command, "Could not set general options")) == null)
153           return 0;
154 
155         // Options across all protocols
156         command = new OpalMessageRef(Opal_API.OpalMessageType.OpalCmdSetProtocolParameters);
157         OpalParamProtocolRef m_protocol = command.GetProtocolParams();
158 
159         m_protocol.UserName = "robertj";
160         m_protocol.DisplayName = "Robert Jongbloed";
161         m_protocol.InterfaceAddresses = "*";
162 
163         if ((response = MySendCommand(command, "Could not set protocol options")) == null)
164           return 0;
165 
166         command = new OpalMessageRef(Opal_API.OpalMessageType.OpalCmdSetProtocolParameters);
167         m_protocol = command.GetProtocolParams();
168 
169         m_protocol.Prefix = "sip";
170         m_protocol.DefaultOptions = "PRACK-Mode=0\nInitial-Offer=false";
171 
172         if ((response = MySendCommand(command, "Could not set SIP options")) == null)
173           return 0;
174 
175         return 1;
176      }
177 
HandleMessages(uint timeout)178     static void HandleMessages(uint timeout)
179     {
180       OpalMessageRef command = new OpalMessageRef();
181       OpalMessageRef response;
182       OpalMessageRef message = new OpalMessageRef();
183 
184       while (context.GetMessage(message, timeout))
185       {
186         switch (message.GetMessageType())
187         {
188           case Opal_API.OpalMessageType.OpalIndRegistration:
189             OpalStatusRegistrationRef m_param = message.GetRegistrationStatus();
190 
191             switch (m_param.Status)
192             {
193               case Opal_API.OpalRegistrationStates.OpalRegisterRetrying:
194                 Console.WriteLine("Trying registration to {0}.", m_param.ServerName);
195                 break;
196               case Opal_API.OpalRegistrationStates.OpalRegisterRestored:
197                 Console.WriteLine("Registration of {0} restored.",m_param.ServerName);
198                 break;
199               case Opal_API.OpalRegistrationStates.OpalRegisterSuccessful:
200                 Console.WriteLine("Registration of {0} successful.", m_param.ServerName);
201                 break;
202               case Opal_API.OpalRegistrationStates.OpalRegisterRemoved:
203                 Console.WriteLine("Unregistered {0}.", m_param.ServerName);
204                 break;
205               case Opal_API.OpalRegistrationStates.OpalRegisterFailed:
206                 if (m_param.Error == null || m_param.Error.Length == 0)
207                   Console.WriteLine("Registration of {0} failed.", m_param.ServerName);
208                 else
209                   Console.WriteLine("Registration of {0} error: {1}",m_param.ServerName, m_param.Error);
210                 break;
211             }
212             break;
213 
214           case Opal_API.OpalMessageType.OpalIndLineAppearance:
215             OpalStatusLineAppearanceRef m_lineStatus = message.GetLineAppearance();
216             switch (m_lineStatus.State)
217             {
218               case Opal_API.OpalLineAppearanceStates.OpalLineIdle:
219                 Console.WriteLine("Line {0} available.", m_lineStatus.Line);
220                 break;
221               case Opal_API.OpalLineAppearanceStates.OpalLineTrying:
222                 Console.WriteLine("Line {0} in use.", m_lineStatus.Line);
223                 break;
224               case Opal_API.OpalLineAppearanceStates.OpalLineProceeding:
225                 Console.WriteLine("Line {0} calling.", m_lineStatus.Line);
226                 break;
227               case Opal_API.OpalLineAppearanceStates.OpalLineRinging:
228                 Console.WriteLine("Line {0} ringing.", m_lineStatus.Line);
229                 break;
230               case Opal_API.OpalLineAppearanceStates.OpalLineConnected:
231                 Console.WriteLine("Line {0} connected.", m_lineStatus.Line);
232                 break;
233               case Opal_API.OpalLineAppearanceStates.OpalLineSubcribed:
234                 Console.WriteLine("Line {0} subscription successful.", m_lineStatus.Line);
235                 break;
236               case Opal_API.OpalLineAppearanceStates.OpalLineUnsubcribed:
237                 Console.WriteLine("Unsubscribed line {0}.", m_lineStatus.Line);
238                 break;
239             }
240             break;
241 
242           case Opal_API.OpalMessageType.OpalIndIncomingCall:
243             OpalStatusIncomingCallRef incomingCall = message.GetIncomingCall();
244 
245             Console.WriteLine("Incoming call from \"{0}\", \"{1}\" to \"{2}\", handled by \"{3}\".",
246                    incomingCall.RemoteDisplayName,
247                    incomingCall.RemoteAddress,
248                    incomingCall.CalledAddress,
249                    incomingCall.LocalAddress);
250             if (currentCallToken == null)
251             {
252               command = new OpalMessageRef(Opal_API.OpalMessageType.OpalCmdAnswerCall);
253               OpalParamAnswerCallRef answerCall = command.GetAnswerCall();
254               answerCall.CallToken = incomingCall.CallToken;
255               OpalParamProtocolRef overrides = new OpalParamProtocolRef(answerCall.Overrides);
256               overrides.UserName = "test123";
257               overrides.DisplayName = "Test Called Party";
258               answerCall.Overrides = overrides.Param;
259 
260               MySendCommand(command, "Could not answer call");
261             }
262             else
263             {
264               command = new OpalMessageRef(Opal_API.OpalMessageType.OpalCmdClearCall);
265               OpalParamCallClearedRef clearCall = command.GetClearCall();
266               OpalStatusIncomingCallRef m_incomingCall = message.GetIncomingCall();
267               clearCall.CallToken = m_incomingCall.CallToken;
268               clearCall.Reason = Opal_API.OpalCallEndReason.OpalCallEndedByLocalBusy;
269               MySendCommand(command, "Could not refuse call");
270             }
271             break;
272 
273           case Opal_API.OpalMessageType.OpalIndProceeding:
274             Console.WriteLine("Proceeding.");
275             break;
276 
277           case Opal_API.OpalMessageType.OpalIndAlerting:
278             Console.WriteLine("Ringing.");
279             break;
280 
281           case Opal_API.OpalMessageType.OpalIndEstablished:
282             Console.WriteLine("Established.");
283 
284             if (playScript != null)
285             {
286               Console.WriteLine("Playing {0}", playScript);
287 
288               command = new OpalMessageRef(Opal_API.OpalMessageType.OpalCmdTransferCall);
289               OpalParamSetUpCallRef m_callSetUp = command.GetCallSetUp();
290               m_callSetUp.CallToken = currentCallToken;
291               m_callSetUp.PartyA = "pc:";
292               m_callSetUp.PartyB = playScript;
293               MySendCommand(command, "Could not start playing");
294             }
295             break;
296 
297           case Opal_API.OpalMessageType.OpalIndMediaStream:
298             OpalStatusMediaStreamRef m_mediaStream = message.GetMediaStream();
299             Console.WriteLine("Media stream {0} {1} using {2}.", m_mediaStream.Type,
300               m_mediaStream.State == Opal_API.OpalMediaStates.OpalMediaStateOpen ? "opened" : "closed",
301               m_mediaStream.Format);
302             break;
303 
304           case Opal_API.OpalMessageType.OpalIndUserInput:
305             OpalStatusUserInputRef m_userInput = message.GetUserInput();
306             Console.WriteLine("User Input: {0}.", m_userInput.UserInput);
307             break;
308 
309           case Opal_API.OpalMessageType.OpalIndCallCleared:
310             OpalStatusCallClearedRef m_callCleared = message.GetCallCleared();
311             if (m_callCleared.Reason == null)
312               Console.WriteLine("Call cleared.");
313             else
314               Console.WriteLine("Call cleared: {0}", m_callCleared.Reason);
315             break;
316 
317           default:
318             break;
319         }
320       }
321     }
322 
DoCall(string from, string to)323     static int DoCall(string from, string to)
324     {
325       // Example cmd line: call 612@ekiga.net
326       OpalMessageRef command = new OpalMessageRef();
327       OpalMessageRef response = new OpalMessageRef();
328 
329       Console.WriteLine("Calling {0}", to);
330 
331       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdSetUpCall);
332       OpalParamSetUpCallRef m_callSetUp = command.GetCallSetUp();
333       m_callSetUp.PartyA = from;
334       m_callSetUp.PartyB = to;
335 
336       OpalParamProtocolRef overrides = new OpalParamProtocolRef(new Opal_API.OpalParamProtocol());
337       overrides.DisplayName = "Test Calling Party";
338       m_callSetUp.Overrides = overrides.Param;
339 
340       if ((response = MySendCommand(command, "Could not make call")) == null)
341         return 0;
342 
343       m_callSetUp = response.GetCallSetUp();
344       currentCallToken = m_callSetUp.CallToken;
345 
346       return 1;
347     }
348 
DoMute(bool on)349     static int DoMute(bool on)
350     {
351       // Example cmd line: mute 612@ekiga.net
352       OpalMessageRef command = new OpalMessageRef();
353       OpalMessageRef response = new OpalMessageRef();
354 
355       Console.WriteLine("Mute {0}", on ? "on" : "off");
356 
357       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdMediaStream);
358       OpalStatusMediaStreamRef m_mediaStream = command.GetMediaStream();
359       m_mediaStream.CallToken = currentCallToken;
360       m_mediaStream.Type = "audio out";
361       m_mediaStream.State = on ? Opal_API.OpalMediaStates.OpalMediaStatePause : Opal_API.OpalMediaStates.OpalMediaStateResume;
362       if ((response = MySendCommand(command, "Could not mute call")) == null)
363         return 0;
364 
365       return 1;
366     }
367 
DoHold()368     static int DoHold()
369     {
370       // Example cmd line: hold 612@ekiga.net
371       OpalMessageRef command = new OpalMessageRef();
372       OpalMessageRef response;
373 
374       Console.WriteLine("Hold");
375 
376       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdHoldCall);
377       command.SetCallToken(currentCallToken);
378 
379       if ((response = MySendCommand(command, "Could not hold call")) == null)
380         return 0;
381 
382       heldCallToken = currentCallToken;
383       currentCallToken = null;
384 
385       return 1;
386     }
387 
DoTransfer(string to)388     static int DoTransfer(string to)
389     {
390       // Example cmd line: transfer fred@10.0.1.11 noris@10.0.1.15
391       OpalMessageRef command = new OpalMessageRef();
392       OpalMessageRef response;
393 
394       Console.WriteLine("Transferring to {0}", to);
395 
396       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdTransferCall);
397       OpalParamSetUpCallRef m_callSetup = command.GetCallSetUp();
398       m_callSetup.PartyB = to;
399       m_callSetup.CallToken = currentCallToken;
400 
401       if ((response = MySendCommand(command, "Could not transfer call")) == null)
402         return 0;
403 
404       return 1;
405     }
406 
DoRegister(string aor, string pwd)407     static int DoRegister(string aor, string pwd)
408     {
409       // Example cmd line: register robertj@ekiga.net secret
410       OpalMessageRef command = new OpalMessageRef();
411       OpalMessageRef response;
412 
413       Console.WriteLine("Registering {0}", aor);
414 
415       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdRegistration);
416       OpalParamRegistrationRef m_registrationInfo = command.GetRegistrationInfo();
417 
418       if (!aor.Contains(':'))
419       {
420         m_registrationInfo.Protocol = "h323";
421         m_registrationInfo.Identifier = aor;
422       }
423       else
424       {
425         m_registrationInfo.Protocol = aor;
426         m_registrationInfo.Identifier = aor.Substring(aor.IndexOf(':') + 1);
427       }
428 
429       m_registrationInfo.Password = pwd;
430       m_registrationInfo.TimeToLive = 300;
431 
432       if ((response = MySendCommand(command, "Could not register endpoint")) == null)
433         return 0;
434 
435       return 1;
436     }
437 
DoSubscribe(string package, string aor, string from)438     static int DoSubscribe(string package, string aor, string from)
439     {
440       // Example cmd line: subscribe "dialog;sla;ma" 1501@192.168.1.32 1502@192.168.1.32
441       OpalMessageRef command = new OpalMessageRef();
442       OpalMessageRef response;
443 
444       Console.WriteLine("Susbcribing {0}", aor);
445 
446       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdRegistration);
447       OpalParamRegistrationRef m_registrationInfo = command.GetRegistrationInfo();
448 
449       m_registrationInfo.Protocol = "sip";
450       m_registrationInfo.Identifier = aor;
451       m_registrationInfo.HostName = from;
452       m_registrationInfo.EventPackage = package;
453       m_registrationInfo.TimeToLive = 300;
454 
455       if ((response = MySendCommand(command, "Could not subscribe")) == null)
456         return 0;
457 
458       return 1;
459     }
460 
DoRecord(string to, string file)461     static int DoRecord(string to, string file)
462     {
463       // Example cmd line: call 612@ekiga.net
464       OpalMessageRef command = new OpalMessageRef();
465       OpalMessageRef response;
466 
467       Console.WriteLine("Calling {0}", to);
468 
469       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdSetUpCall);
470       OpalParamSetUpCallRef m_callSetUp = command.GetCallSetUp();
471 
472       m_callSetUp.PartyB = to;
473       if ((response = MySendCommand(command, "Could not make call")) == null)
474         return 0;
475 
476       currentCallToken = response.GetCallSetUp().CallToken;
477 
478       Console.WriteLine("Recording {0}", file);
479 
480       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdStartRecording);
481       OpalParamRecordingRef m_recording = command.GetRecording();
482 
483       m_recording.CallToken = currentCallToken;
484       m_recording.File = file;
485       m_recording.Channels = 2;
486 
487       if ((response = MySendCommand(command, "Could not start recording")) == null)
488         return 0;
489 
490       return 1;
491     }
492 
DoPlay(string to, string file)493     static int DoPlay(string to, string file)
494     {
495       // Example cmd line: call 612@ekiga.net
496       OpalMessageRef command = new OpalMessageRef();
497       OpalMessageRef response;
498 
499       Console.WriteLine("Playing {0} to {1}", file, to);
500 
501       playScript = String.Format("ivr:<?xml version=\"1.0\"?><vxml version=\"1.0\"><form id=\"PlayFile\">" +
502                    "<audio src=\"{0}\"/></form></vxml>", file);
503 
504       command.SetMessageType(Opal_API.OpalMessageType.OpalCmdSetUpCall);
505       OpalParamSetUpCallRef m_callSetUp = command.GetCallSetUp();
506       m_callSetUp.PartyB = to;
507 
508       if ((response = MySendCommand(command, "Could not make call")) == null)
509         return 0;
510 
511       currentCallToken = response.GetCallSetUp().CallToken;
512 
513       return 1;
514     }
515 
516     enum Operations
517     {
518       OpListen,
519       OpCall,
520       OpMute,
521       OpHold,
522       OpTransfer,
523       OpConsult,
524       OpRegister,
525       OpSubscribe,
526       OpRecord,
527       OpPlay,
528       NumOperations
529     }
530 
531     static string[] OperationNames = { "listen", "call", "mute", "hold", "transfer", "consult", "register", "subscribe", "record", "play" };
532 
533     static int[] RequiredArgsForOperation = { 1, 2, 2, 2, 3, 3, 2, 2, 2, 2 };
534 
GetOperation(string name)535     static Operations GetOperation(string name)
536     {
537       Operations op;
538 
539       for (op = Operations.OpListen; op < Operations.NumOperations; op++) {
540         if (name == OperationNames[(int)op])
541           break;
542       }
543 
544       return op;
545     }
546 
Main(string[] args)547     static void Main(string[] args)
548     {
549       Operations operation;
550 
551       if (args.Length < 1 || (operation = GetOperation(args[0])) == Operations.NumOperations || args.Length < RequiredArgsForOperation[(int)operation])
552       {
553         Operations op;
554         Console.Write("usage: c_api { ");
555 
556         for (op = Operations.OpListen; op < Operations.NumOperations; op++)
557         {
558           if (op > Operations.OpListen)
559             Console.Write(" | ");
560           Console.Write(OperationNames[(int)op]);
561         }
562 
563         Console.Write(" } [ A-party [ B-party | file ] ]{0}", Environment.NewLine);
564         return;
565       }
566 
567       Console.WriteLine("Initialising.");
568 
569       if (InitialiseOPAL() == 0)
570         return;
571 
572       switch (operation)
573       {
574         case Operations.OpListen:
575           Console.WriteLine("Listening.");
576           HandleMessages(60000);
577           break;
578 
579         case Operations.OpCall:
580           if (args.Length > 3)
581           {
582             if (DoCall(args[2], args[3]) == 0)
583               break;
584           }
585           else
586           {
587             if (DoCall(null, args[2]) ==  0)
588               break;
589           }
590           HandleMessages(15000);
591           break;
592 
593         case Operations.OpMute:
594           if (DoCall(null, args[2]) == 0)
595             break;
596           HandleMessages(15000);
597           if (DoMute(true) == 0)
598             break;
599           HandleMessages(15000);
600           if (DoMute(false) == 0)
601             break;
602           HandleMessages(15000);
603           break;
604 
605         case Operations.OpHold:
606           if (DoCall(null, args[2]) == 0)
607             break;
608           HandleMessages(15000);
609           if (DoHold() == 0)
610             break;
611           HandleMessages(15000);
612           break;
613 
614         case Operations.OpTransfer:
615           if (DoCall(null, args[2]) == 0)
616             break;
617           HandleMessages(15000);
618           if (DoTransfer(args[3]) == 0)
619             break;
620           HandleMessages(15000);
621           break;
622 
623         case Operations.OpConsult:
624           if (DoCall(null, args[2]) == 0)
625             break;
626           HandleMessages(15000);
627           if (DoHold() == 0)
628             break;
629           HandleMessages(15000);
630           if (DoCall(null, args[3]) == 0)
631             break;
632           HandleMessages(15000);
633           if (DoTransfer(heldCallToken) == 0)
634             break;
635           HandleMessages(15000);
636           break;
637 
638         case Operations.OpRegister:
639           if (DoRegister(args[2], args[3]) == 0)
640             break;
641           HandleMessages(15000);
642           break;
643 
644         case Operations.OpSubscribe:
645           if (DoSubscribe(args[2], args[3], args[4]) == 0)
646             break;
647           HandleMessages(int.MaxValue); // More or less forever
648           break;
649 
650         case Operations.OpRecord:
651           if (DoRecord(args[2], args[3]) == 0)
652             break;
653           HandleMessages(int.MaxValue); // More or less forever
654           break;
655 
656         case Operations.OpPlay:
657           if (DoPlay(args[2], args[3]) == 0)
658             break;
659           HandleMessages(int.MaxValue); // More or less forever
660           break;
661 
662         default:
663           break;
664       }
665 
666       Console.WriteLine("Exiting.");
667 
668       context.ShutDown();
669       return;
670     }
671   }
672 }
673