? t ? openmcu.ini ? foo ? Readme-anthony.txt ? web ? new ? ChangeLog ? diff_115.patch ? confserver.py Index: main.cxx =================================================================== RCS file: /export/00/cvsroot/voip/openh323/openmcu/main.cxx,v retrieving revision 1.1.1.3 retrieving revision 1.26 diff -u -r1.1.1.3 -r1.26 --- main.cxx 2003/01/06 23:16:55 1.1.1.3 +++ main.cxx 2003/01/16 07:18:03 1.26 @@ -239,8 +239,10 @@ * */ + #include #include +#include #include "version.h" #include "mscodecs.h" @@ -257,6 +259,38 @@ PCREATE_PROCESS(OpenMcu); +const WORD DefaultHTTPPort = 5719; + +static const char LogLevelKey[] = "PTrace Log Level"; +static const char UserNameKey[] = "Admin User Name"; +static const char PasswordKey[] = "Admin Password"; +static const char HttpPortKey[] = "HTTP Listener Port"; +static const char H323PortKey[] = "H.323 Listener Port"; +static const char ListenInterfaceKey[] = "H.323 Listener Interface"; +static const char LocalUserNameKey[] = "Local User Name"; +static const char GatekeeperWantedKey[] = "Register with Gatekeeper?"; +static const char GatekeeperNameKey[] = "Gatekeeper Name"; +static const char GatekeeperNeededKey[] = "Gatekeeper Required"; +static const char TranslateAddressKey[] = "NAT Translation Address"; +static const char JitterKey[] = "Jitter ([min-]max)"; +static const char G711FramesKey[] = "G.711 Frames"; +static const char GSMFramesKey[] = "GSM Frames"; +#ifndef NO_VIDEO +static const char VideoEnableKey[] = "Enable H.261 Video"; +static const char VideoLargeKey[] = "Enable Large (352x288) Video"; +static const char VideoTxQualityKey[] = "Video Quality (1-31)"; +static const char VideoFillKey[] = "Video Background Fill Rate (1-99)"; +static const char VideoFramesSecKey[] = "Video Frames/sec (1-10"; +#endif +static const char DefaultRoomNameKey[] = "Default Room Name"; +static const char NoDefaultRoomKey[] = "Disable Default Room"; +static const char AudioLoopbackRoomsKey[] = "Users hear own voice in which rooms?"; +static const char EndpointUsernameKey[] = "Endpoint Username"; +static const char XMLRPCServer[] = "Config XMLRPC Server"; +static const char XMLRPCSetupURL[] = "XMLRPC Setup URL"; +static const char XMLRPCTeardownURL[] = "XMLRPC Teardown URL"; + + #define new PNEW // size of a PCM data packet, in samples @@ -270,6 +304,28 @@ #define PCM_BUFFER_SIZE (PCM_BUFFER_LEN * PCM_BUFFER_COUNT) +PXMLRPC_STRUCT_BEGIN(ActionStruct) + PXMLRPC_STRING (ActionStruct, PString, action0); + PXMLRPC_STRING (ActionStruct, PString, action1); + PXMLRPC_STRING (ActionStruct, PString, action2); + PXMLRPC_STRING (ActionStruct, PString, action3); + PXMLRPC_STRING (ActionStruct, PString, action4); + PXMLRPC_STRING (ActionStruct, PString, action5); + PXMLRPC_STRING (ActionStruct, PString, action6); + PXMLRPC_STRING (ActionStruct, PString, action7); + PXMLRPC_STRING (ActionStruct, PString, action8); + PXMLRPC_STRING (ActionStruct, PString, action9); + PXMLRPC_STRING (ActionStruct, PString, actionstar); + PXMLRPC_STRING (ActionStruct, PString, actionhash); +PXMLRPC_STRUCT_END() + +PXMLRPC_STRUCT_BEGIN(ResponseStruct) + PXMLRPC_STRING (ResponseStruct, PString, answerCall); + PXMLRPC_STRING (ResponseStruct, PString, privs); + PXMLRPC_STRING (ResponseStruct, PString, roomID); + PXMLRPC_STRUCT (ResponseStruct, ActionStruct, actions); +PXMLRPC_STRUCT_END() + /////////////////////////////////////////////////////////////// #ifdef LOGGING @@ -320,13 +376,42 @@ } #endif +static PString QuoteHTML(PString original) { + static PRegularExpression AmpRe("&"); + static PRegularExpression LTRe("<"); + static PRegularExpression GTRe(">"); + PINDEX pos=0, sz=0; + while (original.FindRegEx(AmpRe, pos, sz)) + original.Splice("&", pos, sz); + pos = 0; sz = 0; + while (original.FindRegEx(GTRe, pos, sz)) + original.Splice("<", pos, sz); + pos = 0; sz = 0; + while (original.FindRegEx(LTRe, pos, sz)) + original.Splice(">", pos, sz); + return original; +} + /////////////////////////////////////////////////////////////// +PHTTPServiceProcess::Info mcuInfo = {"OpenMCU", + "Anthony Baxter and OpenH323 Project", + MAJOR_VERSION, MINOR_VERSION, PProcess::BUILD_TYPE, + BUILD_NUMBER, __TIME__ __DATE__, + {{ 0 }}, { NULL }, 0, {{ 0 }}, // Only relevent for commercial apps + NULL, //HOME_PAGE + NULL, //EMAIL + "OpenMCU", + NULL, // GIF HTML, use calculated from below + "openmcu.gif", //GIF_NAME, + 200, //GIF_WIDTH, + 60 //GIF_HEIGHT + }; OpenMcu::OpenMcu() - : PProcess("OpenH323 Project", "OpenMCU", - MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER) + : PHTTPServiceProcess(mcuInfo) { + endpoint = NULL; } @@ -334,136 +419,160 @@ { } +void OpenMcu::OnControl() +{ +} + +BOOL OpenMcu::OnStart() +{ + cout << "OpenMcu OnStart called!" << endl; + PTrace::SetStream(&cerr); + PTrace::Initialise(4); + endpoint = new MyH323EndPoint(); + return PHTTPServiceProcess::OnStart(); +} +void OpenMcu::OnStop() +{ + cout << "OpenMcu OnStop called!" << endl; + delete endpoint ; + PHTTPServiceProcess::OnStop(); +} + +void OpenMcu::OnConfigChanged() +{ +} + + void OpenMcu::Main() +{ + cout << "openmcu::main" << endl; + Suspend(); +} + +BOOL OpenMcu::Initialise(const char *msg) { - cout << GetName() + cout << GetName() << " " << msg << endl << " Version " << GetVersion(TRUE) << " by " << GetManufacturer() << " on " << GetOSClass() << ' ' << GetOSName() << " (" << GetOSVersion() << '-' << GetOSHardware() << ")\n"; //PArgList & args = GetArguments(); - PConfigArgs args(GetArguments()); + + PConfig cfg("Parameters"); - args.Parse( - "g-gatekeeper:" "n-no-gatekeeper." - "-require-gatekeeper." "-no-require-gatekeeper." - "q-translate:" - "h-help." - "i-interface:" "-no-interface." - "-save." - "j-jitter:" - "-listenport:" - "-gsmframes:" "-no-gsmframes." - "-g711frames:" "-no-g711frames." -#if PTRACING - "t-trace." - "o-output:" -#endif - "-defaultroom:" "-no-defaultroom." - "u-username:" "-no-username." -#ifndef NO_VIDEO - "v-video." "-no-video." - "-videolarge." - "-videotxquality:" "-no-videotxquality." - "-videofill:" "-no-videofill." - "-videotxfps:" "-no-videotxfps." - "-disable-menu." - "-audio-loopback:" -#endif - , FALSE); - -#if PTRACING - PTrace::Initialise(args.GetOptionCount('t'), - args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL); -#endif - - if (args.HasOption('h')) { - cout << "Usage : " << GetName() << " [options]\n" - "Options:\n" - " -u --username str : Set the local endpoint name to str\n" - " -g --gatekeeper host: Specify gatekeeper host.\n" - " -n --no-gatekeeper : Disable gatekeeper discovery.\n" - " --require-gatekeeper: Exit if gatekeeper discovery fails.\n" - " -q --translate ip : Set external IP address to ip if masQueraded \n" - " -i --interface ip : Bind to a specific interface\n" - " -j --jitter [min-]max : Set minimum (optional) and maximum jitter buffer (in milliseconds).\n" - " --listenport n : Port to listen on for incoming connections(default 1720)\n" - " --g711frames count : Set the number G.711 frames in capabilities (default 30)\n" - " --gsmframes count : Set the number GSM frames in capabilities (default 4)\n" -#if PTRACING - " -t --trace : Enable trace, use multiple times for more detail\n" - " -o --output : File for trace output, default is stderr\n" -#endif - " --save : Save arguments in configuration file\n" -#ifndef NO_VIDEO - " -v --video : Enable H261 video handling\n" - " --videolarge : Set the video size from normal (176x144) to large (352x288).\n" - " --videotxquality n : Select sent video quality,(def 9). 1(good)<=n<=31\n" - " --videofill n : Select number of updated background blocks per frame 1<=n<=99 (2 def)\n" - " --videotxfps n : Maximum number of transmitted video frames per sec 1<10(def)<30\n\n" -#endif - " --defaultroom name : Connections without a room name will join this room\n" - " (Default room is room101)\n" - " --no-defaultroom : Reject connections with no room specified\n" - " --disable-menu : Disable the command line menu\n" - " --audio-loopback name : Let a user hear their own voice echo'ed back in this room\n" - " -h --help : Display this help message\n"; - return; + PTrace::SetLevel(cfg.GetInteger(LogLevelKey, PTrace::GetLevel())); + + // HTTP setup + PString adminUserName = cfg.GetString(UserNameKey); + PString adminPassword = PHTTPPasswordField::Decrypt(cfg.GetString(PasswordKey)); + PHTTPSimpleAuth authority(GetName(), adminUserName, adminPassword); + + PConfigPage * rsrc = new PConfigPage(*this, "Parameters", "Parameters", authority); + + // HTTP authentication username/password + rsrc->Add(new PHTTPStringField(UserNameKey, 25, adminUserName)); + rsrc->Add(new PHTTPPasswordField(PasswordKey, 25, adminPassword)); + + // Log level for messages + rsrc->Add(new PHTTPIntegerField(LogLevelKey,1,5,PTrace::GetLevel())); + + cout << "endpoint init" << endl; + PTRACE(0, "Ptrace output"); + cout << "PTrace currently at " << PTrace::GetLevel() << endl; + + // more setting of options &c. + if (!endpoint->Initialise(cfg, rsrc)) { + cerr << "Endpoint Initialisation failed!" << endl; + return FALSE; + } + + PServiceHTML html("System Parameters"); + rsrc->BuildHTML(html); + html << "Submitting may have bad effects on connected users!" << endl; + httpNameSpace.AddResource(rsrc, PHTTPSpace::Overwrite); + + // Add the status page + httpNameSpace.AddResource(new MainStatusPage(*this, authority), PHTTPSpace::Overwrite); + + PHTML front; + front << PHTML::Title("Welcome to OpenMCU") + << PHTML::Body() + << PHTML::Paragraph() << "
" + << "Welcome" + << PHTML::Paragraph() + << "OpenMCU started at " << PTime() + << "
" + //<< PHTML::Paragraph() + //<< PHTML::HotLink("Parameters") << "Parameters" << PHTML::HotLink() + //<< PHTML::Paragraph() + << PHTML::HotLink("Status") << "Status" << PHTML::HotLink() + << PHTML::HRule() + << PHTML::Body(); + httpNameSpace.AddResource(new PServiceHTTPString("welcome.html", front), + PHTTPSpace::Overwrite); + if (ListenForHTTP(DefaultHTTPPort)) { + cout << "Listening for HTTP on " << DefaultHTTPPort << endl; + } else { + cerr << "Couldn't listen for HTTP on " << DefaultHTTPPort << endl; } + cout << "endpoint listen" << endl; - args.Save("save"); + endpoint->ListenForIncomingCalls(); + endpoint->AwaitTermination(); + cout << "GoodBye. " << endl; + PThread::Current()->Sleep(2000); + cout << "Finished 2 sec sleep" < 240) { - cerr << "error: G.711 frame size must be in range 10 to 240" << endl; - g711Frames = 30; - } +BOOL MyH323EndPoint::Initialise(PConfig & cfg, PConfigPage * rsrc) +{ + PString userName = cfg.GetString(EndpointUsernameKey, + "OpenH323 MCU"); + SetLocalUserName(userName); + + int g711Frames = cfg.GetInteger(G711FramesKey, 30); + if (g711Frames <= 10 || g711Frames > 240) { + cerr << "error: G.711 frame size must be in range 10 to 240" << endl; + g711Frames = 30; } + rsrc->Add(new PHTTPIntegerField(G711FramesKey, 10, 240, g711Frames)); - int gsmFrames = 4; - if (args.HasOption("gsmframes")) { - gsmFrames = args.GetOptionString("gsmframes").AsInteger(); - if (gsmFrames < 1 || gsmFrames > 7) { - cerr << "error: GSM frame size must be in range 1 to 7" << endl; - gsmFrames = 4; - } + int gsmFrames = cfg.GetInteger(GSMFramesKey, 4); + if (gsmFrames < 1 || gsmFrames > 7) { + cerr << "error: GSM frame size must be in range 1 to 7" << endl; + gsmFrames = 4; } + rsrc->Add(new PHTTPIntegerField(GSMFramesKey, 1, 7, gsmFrames)); H323Capability * gsm = new H323_GSM0610Capability; H323Capability * msgsm = new MicrosoftGSMAudioCapability; - H323Capability * ulaw64 = new H323_G711Capability(H323_G711Capability::muLaw, H323_G711Capability::At64k); - H323Capability * alaw64 = new H323_G711Capability(H323_G711Capability::ALaw, H323_G711Capability::At64k); - H323Capability * lpc10 = new H323_LPC10Capability(endpoint); + H323Capability * ulaw64 = new H323_G711Capability(H323_G711Capability::muLaw, + H323_G711Capability::At64k); + H323Capability * alaw64 = new H323_G711Capability(H323_G711Capability::ALaw, + H323_G711Capability::At64k); + H323Capability * lpc10 = new H323_LPC10Capability(*this); H323Capability * speex3 = new SpeexNarrow3AudioCapability(); // 8k H323Capability * speex5 = new SpeexNarrow5AudioCapability(); // 15k + H323Capability * userinput = new H323_UserInputCapability( + H323_UserInputCapability::BasicString); - endpoint.SetCapability(0, 0, speex3); - endpoint.SetCapability(0, 0, speex5); - - endpoint.SetCapability(0, 0, gsm); + SetCapability(0, 0, userinput); + SetCapability(0, 0, speex3); + SetCapability(0, 0, speex5); + SetCapability(0, 0, gsm); gsm->SetTxFramesInPacket(gsmFrames); - - endpoint.SetCapability(0, 0, msgsm); + SetCapability(0, 0, msgsm); msgsm->SetTxFramesInPacket(gsmFrames); - - endpoint.SetCapability(0, 0, ulaw64); + SetCapability(0, 0, ulaw64); ulaw64->SetTxFramesInPacket(g711Frames); - - endpoint.SetCapability(0, 0, alaw64); + SetCapability(0, 0, alaw64); alaw64->SetTxFramesInPacket(g711Frames); - - endpoint.SetCapability(0, 0, lpc10); + SetCapability(0, 0, lpc10); + /* TODO if (args.HasOption('j')) { unsigned minJitter; unsigned maxJitter; @@ -474,55 +583,58 @@ } else { maxJitter = delays[0].AsUnsigned(); - minJitter = PMIN(endpoint.GetMinAudioJitterDelay(), maxJitter); + minJitter = PMIN(GetMinAudioJitterDelay(), maxJitter); } if (minJitter >= 20 && minJitter <= maxJitter && maxJitter <= 1000) - endpoint.SetAudioJitterDelay(minJitter, maxJitter); + SetAudioJitterDelay(minJitter, maxJitter); else { cerr << "Jitter should be between 20 and 1000 milliseconds." << endl; return; } } + */ // start the H.323 listener H323ListenerTCP * listener; - - WORD listenPort = H323EndPoint::DefaultTcpPort; - if (args.HasOption("listenport")) - listenPort = (WORD)args.GetOptionString("listenport").AsInteger(); + WORD listenPort = cfg.GetInteger(H323PortKey, H323EndPoint::DefaultTcpPort); + rsrc->Add(new PHTTPIntegerField(H323PortKey, 1, 32767, listenPort)); - if (args.GetOptionString('i').IsEmpty()) { + PString listenAddress = cfg.GetString(ListenInterfaceKey); + if (listenAddress.IsEmpty()) { PIPSocket::Address interfaceAddress(INADDR_ANY); - listener = new H323ListenerTCP(endpoint, interfaceAddress, listenPort); + listener = new H323ListenerTCP(*this, interfaceAddress, listenPort); } else { - PIPSocket::Address interfaceAddress(args.GetOptionString('i')); - listener = new H323ListenerTCP(endpoint, interfaceAddress, listenPort); + PIPSocket::Address interfaceAddress(listenAddress); + listener = new H323ListenerTCP(*this, interfaceAddress, listenPort); } - if (!endpoint.StartListener(listener)) { + + if (!StartListener(listener)) { // TODO LOG cout << "Could not open H.323 listener port on " << listener->GetListenerPort() << endl; - delete listener; - return; + delete listener; + return FALSE; } - + rsrc->Add(new PHTTPStringField(ListenInterfaceKey, 40, listenAddress)); cout << "Listening on port " << listener->GetListenerPort() << endl; - - if (args.GetOptionString('q').IsEmpty()) { - endpoint.behind_masq = FALSE; - } else { - endpoint.masqAddressPtr = new PIPSocket::Address(args.GetOptionString('q')); - endpoint.behind_masq = TRUE; - cout << "Masquerading as address " << *(endpoint.masqAddressPtr) << endl; - } - + // TODO + behind_masq = FALSE; + //if (args.GetOptionString('q').IsEmpty()) { + // behind_masq = FALSE; + //} else { + // masqAddressPtr = new PIPSocket::Address(args.GetOptionString('q')); + // behind_masq = TRUE; + // cout << "Masquerading as address " << *(masqAddressPtr) << endl; + //} +#if 0 // TODO #ifndef NO_VIDEO // If videoLarge was specified + if (args.HasOption("videolarge")) { - endpoint.videoLarge = TRUE; - endpoint.SetVideoSize(352,288); + videoLarge = TRUE; + SetVideoSize(352,288); } else { - endpoint.videoLarge = FALSE; + videoLarge = FALSE; } if (args.HasOption('v')) { @@ -531,96 +643,103 @@ // this is the only way I know of at present to transmit // CIF size video - pez // Do not allow CIF video if size is medium - if ( endpoint.videoLarge ) - endpoint.SetCapability(0, 1, new H323_H261Capability(0, 4, FALSE, FALSE, 6217)); // CIF - endpoint.SetCapability(0, 1, new H323_H261Capability(2, 0, FALSE, FALSE, 6217)); // QCIF + if ( videoLarge ) + SetCapability(0, 1, new H323_H261Capability(0, 4, FALSE, FALSE, 6217)); // CIF + SetCapability(0, 1, new H323_H261Capability(2, 0, FALSE, FALSE, 6217)); // QCIF } - endpoint.EnableVideoReception(args.HasOption('v')); + EnableVideoReception(args.HasOption('v')); int videoTxQual = 10; if (args.HasOption("videotxquality")) videoTxQual = args.GetOptionString("videotxquality").AsInteger(); - endpoint.videoTxQuality = PMAX(1, PMIN(31, videoTxQual)); + videoTxQuality = PMAX(1, PMIN(31, videoTxQual)); int videoF = 2; if (args.HasOption("videofill")) videoF = args.GetOptionString("videofill").AsInteger(); - endpoint.videoFill = PMAX(1, PMIN(99, videoF)); + videoFill = PMAX(1, PMIN(99, videoF)); int videoFPS = 10; if (args.HasOption("videotxfps")) videoFPS = args.GetOptionString("videotxfps").AsInteger(); - endpoint.videoFramesPS = PMAX(1,PMIN(30,videoFPS)); + videoFramesPS = PMAX(1,PMIN(30,videoFPS)); +#endif #endif - if (args.HasOption('g')) { - PString gkName = args.GetOptionString('g'); - if (endpoint.SetGatekeeper(gkName, new H323TransportUDP(endpoint))) - cout << "Gatekeeper set: " << *endpoint.GetGatekeeper() << endl; - else { + PString gkName = cfg.GetString(GatekeeperNameKey); + rsrc->Add(new PHTTPStringField(GatekeeperNameKey, 40, gkName)); + BOOL gkWanted = cfg.GetBoolean(GatekeeperWantedKey, TRUE); + rsrc->Add(new PHTTPBooleanField(GatekeeperWantedKey, gkWanted)); + BOOL gkNeeded = cfg.GetBoolean(GatekeeperNeededKey, FALSE); + rsrc->Add(new PHTTPBooleanField(GatekeeperNeededKey, gkNeeded)); + + // Now actually try and do gatekeeper stuff + if (!gkName.IsEmpty()) { + if (SetGatekeeper(gkName, new H323TransportUDP(*this))) { + cout << "Gatekeeper set: " << *GetGatekeeper() << endl; + } else { cout << "Error registering with gatekeeper at \"" << gkName << '"' << endl; - return; + return FALSE; } - } - else if (!args.HasOption('n')) { + } else if (gkWanted) { cout << "Searching for gatekeeper..." << flush; - if (endpoint.DiscoverGatekeeper(new H323TransportUDP(endpoint))) - cout << "\nGatekeeper found: " << *endpoint.GetGatekeeper() << endl; - else { - cout << "\nNo gatekeeper found." << endl; - if (args.HasOption("require-gatekeeper")) - return; + if (DiscoverGatekeeper(new H323TransportUDP(*this))) { + cout << "\nGatekeeper found: " << *GetGatekeeper() << endl; } - } - - PString roomName = "room101"; - if (args.HasOption("defaultroom")) { - roomName = args.GetOptionString("defaultroom"); - } - if (args.HasOption("no-defaultroom")) { - roomName = ""; + } else { + cout << "\nNo gatekeeper found." << endl; + if (cfg.GetBoolean(GatekeeperNeededKey, FALSE)) { + return FALSE; + } } - endpoint.SetDefaultRoomName(roomName); + PString roomName = cfg.GetString(DefaultRoomNameKey, "room101"); + rsrc->Add(new PHTTPStringField(DefaultRoomNameKey, 40, roomName)); - if (args.HasOption("disable-menu")) { - endpoint.hasMenu = FALSE; - } else { - endpoint.hasMenu = TRUE; - } + xmlrpcServer = cfg.GetString(XMLRPCServer, ""); + rsrc->Add(new PHTTPStringField(XMLRPCServer, 50, xmlrpcServer)); + xmlrpcSetupURL = cfg.GetString(XMLRPCSetupURL, ""); + rsrc->Add(new PHTTPStringField(XMLRPCSetupURL, 40, xmlrpcSetupURL)); + xmlrpcTeardownURL = cfg.GetString(XMLRPCTeardownURL, ""); + rsrc->Add(new PHTTPStringField(XMLRPCTeardownURL, 40, xmlrpcTeardownURL)); + BOOL defRoomOk = cfg.GetBoolean(NoDefaultRoomKey, FALSE); + if (!defRoomOk) + roomName = ""; + SetDefaultRoomName(roomName); + rsrc->Add(new PHTTPBooleanField(NoDefaultRoomKey, defRoomOk)); - if (args.HasOption("audio-loopback")) { - endpoint.audioLoopbackRoom = args.GetOptionString("audio-loopback"); + hasMenu = FALSE; + /* + if (args.HasOption("disable-menu")) { + hasMenu = FALSE; } else { - endpoint.audioLoopbackRoom = ""; + hasMenu = TRUE; } - + */ - cout << "Codecs (in preference order):\n" << setprecision(2) << endpoint.GetCapabilities() << endl << endl; + audioLoopbackRoom = cfg.GetString(AudioLoopbackRoomsKey, ""); + rsrc->Add(new PHTTPStringField(AudioLoopbackRoomsKey, + 25, + audioLoopbackRoom, + "* for all rooms, blank for none")); - endpoint.ListenForIncomingCalls(); - endpoint.AwaitTermination(); - cout << "GoodBye. " << endl; + cout << "Codecs (in preference order):\n" << setprecision(2) << GetCapabilities() << endl << endl; - PThread::Current()->Sleep(2000); - cout << "Finished 2 sec sleep" <GetConnectionStartTime(); - cout << "Connected to : " << conn->GetRemotePartyName() << " for " - << setw(5) << setprecision(0) << (now - callStart) << " mins " <GetSession(RTP_Session::DefaultAudioSessionID); - if (session == NULL) { - cout << " : " - << "Sending audio with " - << conn->GetAudioTransmitCodecName() - << endl; - cout << " : " - << "Receiving audio with " - << conn->GetAudioReceiveCodecName() - << endl; - } else { - cout << " : " - << session->GetPacketsSent() << '/' - << session->GetOctetsSent() - << " audio packets/bytes sent (" - << conn->GetAudioTransmitCodecName() - << ")" << endl; - cout << " : " - << session->GetPacketsReceived() << '/' - << session->GetOctetsReceived() - << " audio packets/bytes received (" - << conn->GetAudioReceiveCodecName() - << ")" << endl; - } -#ifndef NO_VIDEO - cout << " : " - << "Sending video with " - << conn->GetVideoTransmitCodecName() - << endl; - cout << " : " - << "Receiving video with " - << conn->GetVideoReceiveCodecName() - << endl; -#endif - conn->Unlock(); - } - } - } - } - memberMutex.Signal(); - } break; #ifndef NO_VIDEO case 'v' : @@ -826,6 +881,88 @@ } } +PString MyH323EndPoint::SimpleStatus() +{ + PStringStream statout; + memberMutex.Wait(); + + // ROOM: get number of rooms + PINDEX rooms = memberListDict.GetSize(); + PINDEX idx; + + if (rooms == 0) { + statout << "No statistics available" << endl; + } else { + + for (idx = 0; idx < rooms; idx++) { + // ROOM: get list of members + PStringList & memberList = memberListDict.GetDataAt(idx); + + // ROOM: get count of members in a room + statout << "Statistics for " << memberList.GetSize() + << " connected parties in room " << (idx+1) << " of "<< rooms + // ROOM: get room ID + << " with room id " << memberListDict.GetKeyAt(idx) << endl; + + PINDEX i; + for (i = 0; i < memberList.GetSize(); i++) { + PString token = memberList[i]; + MyH323Connection * conn = (MyH323Connection *)FindConnectionWithLock( + token); + if (conn != NULL) { + PTime now; + PTime callStart = conn->GetConnectionStartTime(); + statout << "Connected to : " << conn->GetRemotePartyName() + << " for " << setw(5) << setprecision(0) + << (now - callStart) << " mins " <GetCallerID() << endl; + statout << "Muted : " << conn->GetMutedStatus(); + statout << " Last DTMF key : " << conn->GetLastInput() << endl; + RTP_Session * session = conn->GetSession( + RTP_Session::DefaultAudioSessionID); + if (session == NULL) { + statout << " : " + << "Sending audio with " + << conn->GetAudioTransmitCodecName() + << endl; + statout << " : " + << "Receiving audio with " + << conn->GetAudioReceiveCodecName() + << endl; + } else { + statout << " : " + << session->GetPacketsSent() << '/' + << session->GetOctetsSent() + << " audio packets/bytes sent (" + << conn->GetAudioTransmitCodecName() + << ")" << endl; + statout << " : " + << session->GetPacketsReceived() << '/' + << session->GetOctetsReceived() + << " audio packets/bytes received (" + << conn->GetAudioReceiveCodecName() + << ")" << endl; + } +#ifndef NO_VIDEO + statout << " : " + << "Sending video with " + << conn->GetVideoTransmitCodecName() + << endl; + statout << " : " + << "Receiving video with " + << conn->GetVideoReceiveCodecName() + << endl; +#endif + conn->Unlock(); + } + } + } + } + memberMutex.Signal(); + return PString(statout); +} + void MyH323EndPoint::SetDefaultRoomName(PString roomName) { defaultRoomName = roomName; @@ -842,12 +979,22 @@ PString newToken = newMember->GetCallToken(); PString newRoomID = newMember->GetRoomID(); + // ROOM: creates a new room. if (!memberListDict.Contains(newRoomID)) memberListDict.SetAt(newRoomID, new PStringList); + if (!roomDict.Contains(newRoomID)) { + OpenMCURoom *nr = new OpenMCURoom(newRoomID); + nr->SetMuted(FALSE); + roomDict.SetAt(newRoomID, nr); + } PStringList & memberList = memberListDict[newRoomID]; + // ROOM: adds a user to a room. memberList.AppendString(newToken); + OpenMCURoom *nr = roomDict.GetAt(newRoomID); + newMember->SetRoom(nr); + #ifndef NO_VIDEO if (!spokenListDict.Contains(newRoomID)) spokenListDict.SetAt(newRoomID, new PStringList); @@ -890,7 +1037,6 @@ } } } - } void MyH323EndPoint::RemoveMember(MyH323Connection * oldConn) @@ -899,7 +1045,7 @@ PString oldToken = oldConn->GetCallToken(); PString oldRoomID = oldConn->GetRoomID(); - // get the list of members of this room + // ROOM: get the list of members of this room PStringList & memberList = memberListDict[oldRoomID]; PINDEX i; @@ -911,6 +1057,7 @@ if (keyIndex != P_MAX_INDEX) spokenList.RemoveAt(keyIndex); + // ROOM: remove entry if no members left if (spokenList.GetSize() == 0) spokenListDict.RemoveAt(oldRoomID); @@ -936,13 +1083,19 @@ } } - // remove this connection from the member list + // ROOM: remove this connection from the room member list memberList.RemoveAt(memberList.GetStringsIndex(oldToken)); - // if there are no more members in this room, delete the room + // ROOM: if there are no more members in this room, delete the room if (memberList.GetSize() == 0) { - cout << "Room " << oldRoomID << " is now empty" << endl; - memberListDict.RemoveAt(oldRoomID); // Note: memberList is now invalid + OpenMCURoom *room = roomDict.GetAt(oldRoomID); + if (!room->GetStickyFlag()) { + cout << "Room " << oldRoomID << " is now empty" << endl; + memberListDict.RemoveAt(oldRoomID); + delete room; + } else { + cout << "Room " << oldRoomID << " is now empty, but sticky." << endl; + } } } @@ -979,11 +1132,13 @@ { PWaitAndSignal mutex(memberMutex); - //The spokenList construct is required to determine who were the last - //to speak, which is needed for determining which four videoimages are displayed. - //spokenList contains a sorted list of when the last packet was received from - //which connection. Thus, when an audio packet is received from a connection, - //the name of the connection is moved to the end of the list. + // The spokenList construct is required to determine who were the last + // to speak, which is needed for determining which four videoimages + // are displayed. + // spokenList contains a sorted list of when the last packet was + // received from which connection. Thus, when an audio packet is + // received from a connection, the name of the connection is moved to + // the end of the list. // If someone new comes along, everyone moves down. #ifndef NO_VIDEO @@ -1011,6 +1166,7 @@ processAudio: #endif + // ROOM: get list of members in a room to send audio to them. PStringList & memberList = memberListDict[roomID]; PINDEX i; @@ -1040,6 +1196,7 @@ { PWaitAndSignal mutex(memberMutex); + // ROOM: get list of members to figure out who's generated audio PStringList & memberList = memberListDict[roomID]; PINDEX i; @@ -1117,10 +1274,21 @@ aborted = FALSE; cout << "Opening connection" << endl; + room = NULL; } MyH323Connection::~MyH323Connection() { + + if (!ep.GetXMLRPCServer().IsEmpty()) + { + PXMLRPC rpc(ep.GetXMLRPCServer()); + PXMLRPCBlock request(ep.GetXMLRPCTeardownURL()); + PXMLRPCBlock response; + request.AddParam(QuoteHTML(GetCallToken())); // call token + rpc.MakeRequest(request, response); // don't care about response + } + cout << "Closing connection" << endl; #ifdef LOGGING @@ -1190,6 +1358,12 @@ roomID = defaultRoomName; } } + if (!setupPDU.GetQ931().GetCallingPartyNumber(callerID)) { + cout << "Couldn't get CallingPartyNumber" << endl; + callerID = ""; + } else { + cout << "Got CallingPartyNumber " << callerID << endl; + } PString product = "Unknown"; @@ -1203,10 +1377,128 @@ if (vendorInfo.HasOptionalField(H225_VendorIdentifier::e_versionId)) product = product + "/" + vendorInfo.m_versionId.AsString(); } - + + if (!ep.GetXMLRPCServer().IsEmpty()) + { + PXMLRPC rpc(ep.GetXMLRPCServer()); + PXMLRPCBlock request(ep.GetXMLRPCSetupURL()); + + PXMLRPCBlock response; + PString srcnum, dstnum; + + if (!setupPDU.GetSourceE164(srcnum)) + srcnum = "Unknown"; + if (!setupPDU.GetDestinationE164(dstnum)) + dstnum = "Unknown"; + + request.AddParam(QuoteHTML(GetCallToken())); // call token + request.AddParam(QuoteHTML(caller)); // caller name + request.AddParam(QuoteHTML(setupPDU.GetQ931().GetDisplayName())); + request.AddParam(QuoteHTML(GetControlChannel().GetRemoteAddress())); // + request.AddParam(QuoteHTML(srcnum)); + + request.AddParam(QuoteHTML(dstnum)); + request.AddParam(QuoteHTML(roomID)); + + if (!rpc.MakeRequest(request, response)) { + cerr << "RPC Conf Setup FAILED: " + << rpc.GetFaultCode() << ", " + << rpc.GetFaultText() + << endl; + aborted = TRUE; return AnswerCallDenied; + } + cout << "Got XMLRPC response" << endl; + // Now parse the response. We should get two structs, one with + // info on what to do, and an optional second with actions + PString type, val; + PXMLRPCVariableBase *var; + + + + if (response.GetParamCount() == 1) { // For now - need to support Array + if (response.GetParam(0, type, val)) { + if (type == "struct") { + + ResponseStruct dict; + + response.GetParam(0, dict); + + var = dict.GetVariable("answerCall"); + if (!var) { + cerr << "no answerCall in response" << endl; + aborted = TRUE; return AnswerCallDenied; + } + if (var->ToString() != "OK") { + cerr << "got answer!=OK -> " << var->ToString() << endl; + aborted = TRUE; return AnswerCallDenied; + } + + var = dict.GetVariable("roomID"); + if (var && !var->ToString().IsEmpty()) { + cout << "roomID: " << roomID << " -> " << var->ToString() << endl; + roomID = var->ToString(); + } + + roomOwner = FALSE; + var = dict.GetVariable("privs"); + if (var && !var->ToString().IsEmpty()) { + cout << "privs: " << var->ToString() << endl; + if ( var->ToString() == "owner" ) + { + cout << "setting owner flag to true" << endl; + roomOwner = TRUE; + } + } + if (dict.actions.GetNumVariables()) + cout << "Got actions dict with " << dict.actions.GetNumVariables() << endl; + cout << "dict: "<< dict.actions << endl; + int i; + PString actionval; + for (i=0;i<10;i++) { + actionval = dict.actions.GetVariable("action"+PString(i))->ToString(); + if (!actionval.IsEmpty()) + actions.SetAt(PString(i),(const char *)actionval); + } + actionval = dict.actions.GetVariable("actionstar")->ToString(); + if (!actionval.IsEmpty()) + actions.SetAt("*",(const char *)actionval); + actionval = dict.actions.GetVariable("actionhash")->ToString(); + if (!actionval.IsEmpty()) + actions.SetAt("#",(const char *)actionval); + } else { + cerr << "response: got " << type << " instead of struct" << endl; + aborted = TRUE; return AnswerCallDenied; + } + } else { + cerr << "error: getting struct 0 " << response.GetFaultText() << endl; + aborted = TRUE; return AnswerCallDenied; + } +#if 0 + if (response.GetParam(1, type, val)) { + if (type == "struct") { + PStringToString actions; + response.GetParam(0, actions); + } else { + cerr << "actions: got " << type << " instead of struct" << endl; + aborted = TRUE; return AnswerCallDenied; + } + } else { + cerr << "error: getting actions" << response.GetFaultText() << endl; + aborted = TRUE; return AnswerCallDenied; + } + cout << endl; +#endif + } else { + // will need to be 2, eventually. + cerr << "got " << response.GetParamCount() << " params, not 1" << endl; + aborted = TRUE; return AnswerCallDenied; + } + } cout << "Accepting call from " << caller << " using " << product << " with room id " << roomID << endl; connected = TRUE; + userMuted = FALSE; // by default, start un-muted + roomMuted = FALSE; // by default, start un-muted ep.AddMember(this); return AnswerCallNow; @@ -1328,9 +1620,55 @@ BOOL MyH323Connection::OnIncomingAudio(const void * buffer, PINDEX amount) { + if (GetMuted()) + return TRUE; return ep.WriteAudio(GetCallToken(), buffer, amount, roomID); + + +} + +void MyH323Connection::OnUserInputTone(char tone, + unsigned duration, + unsigned logicalChannel, + unsigned rtpTimestamp) +{ + //cout << "Got user tone " << tone << endl; + endpoint.OnUserInputTone(*this, tone,duration,logicalChannel, rtpTimestamp); +} + +void MyH323Connection::OnUserInputString(const PString & value) +{ + cout << "Got user input " << value << " from " << callerID << endl; + lastInput = value; + PString act; + if (actions.Contains(lastInput)) { + act = actions[lastInput]; + if (act == "toggleMute") { + // user mute. + userMuted = !userMuted; + cout << "got (un)mute request from " << callerID ; + cout << ", now " << GetMutedStatus() << endl; + } else if (lastInput == "roomMute") { + // owner mutes room + if (roomOwner) { + cout << "got room mute request from " << callerID << endl; + room->SetMuted(TRUE); + } + } else if (lastInput == "roomUnmute") { + // owner unmutes room + if (roomOwner) { + cout << "got room unmute request from " << callerID << endl; + room->SetMuted(FALSE); + } + } + } } +void MyH323Connection::SetRoom(OpenMCURoom *newRoom) { + // Set the current room. + room = newRoom; +} + #ifndef NO_VIDEO BOOL MyH323Connection::OnOutgoingVideo(void * buffer, PINDEX & amount) { @@ -1362,10 +1700,12 @@ { PWaitAndSignal mutex(audioMutex); + cout << "Removing audio buffer for " << token << " from connection " << GetCallToken() << endl; audioBuffers.RemoveAt(token); + } @@ -1960,6 +2300,119 @@ //if (headRoom > MAX_HEADROOM) // PThread::Current()->Sleep(headRoom - MIN_HEADROOM); +} + + +MainStatusPage::MainStatusPage(OpenMcu & _app, PHTTPAuthority & auth) + : PServiceHTTPString("Status", "", "text/html; charset=UTF-8", auth), + app(_app) +{ + PHTML html; + + html << PHTML::Title("OpenH323 MCU Status") + << PHTML::Body() + << PHTML::Paragraph() << "
" + << PHTML::TableStart("border=1") + << PHTML::TableRow() + << PHTML::TableHeader() << "Room Number" + << PHTML::TableHeader() << "Number of Calls" + << "" + << PHTML::TableRow() + << PHTML::TableData("NOWRAP") + << "" + << PHTML::TableData("NOWRAP") + << "" + << PHTML::TableData("NOWRAP") + << "" + << PHTML::TableEnd() + << "
" + + << "Old-style statistics dump" + << PHTML::Paragraph() + << "
"
+           << ""
+           << ""
+           << "
" + << PHTML::Paragraph() + << PHTML::Body() ; + string = html; +} + +BOOL MainStatusPage::Post(PHTTPRequest & request, + const PStringToString & data, + PHTML & msg) +{ + PTRACE(2, "Main\tClear call POST received " << data); + msg << "Posted" ; + return TRUE; +} + +PCREATE_SERVICE_MACRO_BLOCK(RoomStatus,P_EMPTY,P_EMPTY,block) +{ + return OpenMcu::Current().GetEndPoint().OnLoadRoomStatus(block); +} + +PCREATE_SERVICE_MACRO_BLOCK(SimpleStatus,P_EMPTY,P_EMPTY,block) +{ + return OpenMcu::Current().GetEndPoint().OnLoadSimpleStatus(block); +} + +static void SpliceMacro(PString & text, + const PString & token, + const PString & value) +{ + PRegularExpression RegEx("?", + PRegularExpression::Extended|PRegularExpression::IgnoreCase); + PINDEX pos, len; + while (text.FindRegEx(RegEx, pos, len)) + { + text.Splice(value, pos, len); + } +} + +PString MyH323EndPoint::OnLoadSimpleStatus(const PString & htmlBlock) +{ + return SimpleStatus(); +} + +PString MyH323EndPoint::OnLoadRoomStatus(const PString & htmlBlock) +{ + PINDEX i; + PString substitution; + // ROOM: get number of active users. bodgy. + PINDEX roomcount = memberListDict.GetSize(); + for (i=0; i< roomcount; i++) + { + PString insert = htmlBlock; // copy macro section + PStringList & roomMembers = memberListDict.GetDataAt(i); + SpliceMacro(insert, "RoomNumber", memberListDict.GetKeyAt(i)); + SpliceMacro(insert, "ActiveUsers", roomMembers.GetSize()); + substitution += insert; + } + + return substitution; + +} + +OpenMCURoom::OpenMCURoom(PString name) { + creationTime = PTime(); + roomName = name; + sticky = FALSE; +} + +OpenMCURoom::OpenMCURoom(PString name, BOOL stickyflag) { + creationTime = PTime(); + roomName = name; + sticky = sticky; // does the room hang around? +} + +PStringList OpenMCURoom::GetMembers() { +} + +void OpenMCURoom::AddMember(PString newMember) { +} + +void OpenMCURoom::RemoveMember(PString oldMember) { } // End of File /////////////////////////////////////////////////////////////// Index: main.h =================================================================== RCS file: /export/00/cvsroot/voip/openh323/openmcu/main.h,v retrieving revision 1.1.1.2 retrieving revision 1.17 diff -u -r1.1.1.2 -r1.17 --- main.h 2003/01/03 04:52:35 1.1.1.2 +++ main.h 2003/01/16 07:18:03 1.17 @@ -132,7 +132,9 @@ #include #include +#include + /** ***** Data flow. @@ -181,22 +183,6 @@ and returned to the connection. **/ - -class OpenMcu : public PProcess -{ - PCLASSINFO(OpenMcu, PProcess) - - public: - OpenMcu(); - ~OpenMcu(); - - void Main(); - - protected: - long GetCodec(const PString & codecname); - OpalLineInterfaceDevice * GetDevice(const PString & device); -}; - class AudioBuffer : public PObject { PCLASSINFO(AudioBuffer, PObject); @@ -264,6 +250,33 @@ PDICTIONARY(StringListDict, PString, PStringList); +class OpenMCURoom : public PObject +{ + PCLASSINFO(OpenMCURoom, PObject); + + public: + OpenMCURoom(PString name); + OpenMCURoom(PString name, BOOL sticky); + PTime GetCreationTime() const { return creationTime; } + BOOL GetStickyFlag() const { return sticky; } + BOOL GetMuted() const { return ownerMuted; } + void SetMuted(BOOL m) { ownerMuted = m; } + BOOL GetTotalConns() const { return totalConns; } + PStringList GetMembers(); + void AddMember(PString); + void RemoveMember(PString); + + protected: + PString roomName; + PTime creationTime; + PStringList members; + BOOL sticky; // do we stay around even when there's no members? + BOOL ownerMuted; // Is the room muted to non-owners? + unsigned int totalConns; + unsigned int totalMins; +}; + +PDICTIONARY(StringRoomDict, PString, OpenMCURoom); class MyH323EndPoint : public H323EndPoint { @@ -271,7 +284,10 @@ public: MyH323EndPoint(); - + BOOL Initialise(PConfig & cfg, PConfigPage * rsrc); + PString OnLoadRoomStatus(const PString & htmlBlock); + PString OnLoadSimpleStatus(const PString & htmlBlock); + PString SimpleStatus(); // overrides from H323EndPoint virtual H323Connection * CreateConnection(unsigned callReference); @@ -281,7 +297,6 @@ BOOL behind_masq; PIPSocket::Address *masqAddressPtr; - // new functions virtual void ListenForIncomingCalls(); virtual void AwaitTermination(); @@ -294,7 +309,7 @@ BOOL ReadAudio(const PString & token, void * buffer, PINDEX amount, PString roomID); BOOL WriteAudio(const PString & token, const void * buffer, PINDEX amount, PString roomID); #ifndef NO_VIDEO - BOOL DetectNoise(const void * buffer, PINDEX amount); + BOOL DetectNoise(const void * buffer, PINDEX amount); BOOL ReadVideo(const PString & token, void * buffer, PINDEX amount); BOOL WriteVideo(const PString & token, const void * buffer, PINDEX amount, PString roomID); @@ -320,10 +335,16 @@ PString audioLoopbackRoom; BOOL hasMenu; + PString GetXMLRPCServer() const { return xmlrpcServer; } + PString GetXMLRPCSetupURL() const { return xmlrpcSetupURL; } + PString GetXMLRPCTeardownURL() const { return xmlrpcTeardownURL; } + protected: PMutex memberMutex; // Prevents multiple tasks simultaneously using // list of connected nodes. StringListDict memberListDict; + StringRoomDict roomDict; + PString xmlrpcServer, xmlrpcSetupURL, xmlrpcTeardownURL; AudioDelay writeDelay; @@ -339,6 +360,28 @@ PString defaultRoomName; }; +class OpenMcu : public PHTTPServiceProcess +{ + PCLASSINFO(OpenMcu, PHTTPServiceProcess) + + public: + OpenMcu(); + ~OpenMcu(); + void OnControl(); + void OnConfigChanged(); + BOOL OnStart(); + void OnStop(); + BOOL Initialise(const char *initMsg); + void Main(); + static OpenMcu & Current() { return (OpenMcu &)PProcess::Current(); } + MyH323EndPoint & GetEndPoint() const { return *endpoint; } + + protected: + MyH323EndPoint *endpoint; + long GetCodec(const PString & codecname); + OpalLineInterfaceDevice * GetDevice(const PString & device); +}; + class OutgoingAudio : public PChannel { @@ -502,9 +545,13 @@ BOOL OpenVideoChannel( BOOL isEncoding, H323VideoCodec & codec); #endif - AnswerCallResponse OnAnswerCall(const PString &, const H323SignalPDU &, H323SignalPDU &); + AnswerCallResponse OnAnswerCall(const PString &, const H323SignalPDU &, + H323SignalPDU &); BOOL OnSendSignalSetup( H323SignalPDU & callProceedingPDU ); BOOL OnStartLogicalChannel(H323Channel & channel); + void OnUserInputString(const PString & value); + void OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, + unsigned rtpTimestamp); // functions to add and remove audio buffers void AddMember (const PString & token); @@ -530,13 +577,27 @@ #endif PString GetRoomID() const { return roomID; } + PString GetCallerID() const { return callerID; } + PString GetLastInput() const { return lastInput; } PString GetAudioTransmitCodecName() const { return audioTransmitCodecName; } PString GetAudioReceiveCodecName() const { return audioReceiveCodecName; } + BOOL GetRoomOwnerFlag() const { return roomOwner; } + BOOL GetMuted() + { return (room->GetMuted()&&!roomOwner)||userMuted; } + PString GetMutedStatus() + { return (room->GetMuted()&&!roomOwner)?"By Owner": + (userMuted?"By user":"no"); } + void SetRoom(OpenMCURoom *newRoom); + protected: MyH323EndPoint & ep; + OpenMCURoom *room; + PString audioTransmitCodecName, audioReceiveCodecName; + PStringToString actions; + PMutex audioMutex; AudioBufferDict audioBuffers; @@ -547,6 +608,10 @@ BOOL connected; + BOOL roomMuted; + BOOL userMuted; + BOOL roomOwner; + #ifndef NO_VIDEO PString videoTransmitCodecName, videoReceiveCodecName; BOOL hasVideo; @@ -554,21 +619,38 @@ OutgoingVideo * outgoingVideo; #endif - PString roomID; + PString roomID, callerID, lastInput; + BOOL aborted; }; +class MainStatusPage : public PServiceHTTPString +{ + PCLASSINFO(MainStatusPage, PServiceHTTPString); + + public: + MainStatusPage(OpenMcu & app, PHTTPAuthority & auth); + + virtual BOOL Post( + PHTTPRequest & request, + const PStringToString &, + PHTML & msg + ); + + private: + OpenMcu & app; +}; + //////////////////////////////////////////////////// class UserInterfaceThread : public PThread { - PCLASSINFO(UserInterfaceThread, PThread); + PCLASSINFO(UserInterfaceThread, PThread); public: UserInterfaceThread(MyH323EndPoint & end) - : PThread(1000, NoAutoDeleteThread), endpoint(end) { Resume(); } - void Main() - { endpoint.HandleUserInterface(); } + : PThread(1000, NoAutoDeleteThread), endpoint(end) { Resume(); } + void Main() { endpoint.HandleUserInterface(); } protected: MyH323EndPoint & endpoint; };