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