/* * main.h * * A simple H.323 MCU * * Copyright (c) 1993-1998 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Portions are Copyright (C) 1993 Free Software Foundation, Inc. * All Rights Reserved. * * Contributor(s): Derek J Smithies (derek@indranet.co.nz) * * $Log: main.h,v $ * Revision 1.23 2002/11/21 07:55:12 robertj * Removed redundent and incorrect function for making outgoing call. * * Revision 1.22 2002/06/14 08:05:08 dereks * Added tweak to support operation behind a NAT that forwards ports * This will use TranslateTCPAddress to change the address reported * to connections that occur from outside the 192. subnet. Thanks Sahai. * * Revision 1.21 2002/04/18 10:54:34 rogerh * Fix bug in audio mixing reportde by Bob Lindell * AudioBuffer::Read() had been optimised to do reading and mixing and * contained memset()'s which were incorrect. Clean up the code with * a proper Read() method and an optimised ReadAndMix() method. * * Revision 1.20 2001/12/02 08:17:57 rogerh * Change --audio-loopback to take a room name. Only connections to the * specified room will have audio loopback applied. The remainder of the rooms * in OpenMCU will work as normal. * Submitted by Malcolm Caldwell * I hard coded room "loopback" as a loopback room regardless of * the --audio-loopback parameter. * * Revision 1.19 2001/11/22 13:06:38 rogerh * Add --audio-loopback. Users of OpenMCU can hear their own voice echoed back * which is very handy for testing purposes. * * Revision 1.18 2001/07/23 03:55:13 rogerh * Seperate out codec names for audio and video * * Revision 1.17 2001/07/23 03:28:03 rogerh * Display the codec name in the statistics page * * Revision 1.16 2001/05/31 17:01:52 rogerh * Fixes from Dan Johansson to make video work. * Go back to using 'closed' for the Video Classes. This is needed as * the the video classes come from PVideoChannel which does not use os_handle * in its IsOpen() method. Instead, we must define our own IsOpen() method. * Also, back out the size of the image change. * Finally, add a new feature. For the first 4 connections, video from an * endpoint is displayed immediatly rather than waiting until that ep sends * some audio. (handy for endpoints with video only and with no talking) * * Revision 1.15 2001/05/31 14:29:29 rogerh * Add --disable-menu to OpenMCU * * Revision 1.14 2001/05/08 13:43:11 rogerh * Connections without a room name will now join the default room (room101) * Handy for NetMeeting users who cannot specify the room to OpenMCU. * Add --defaultroom to change the default room name. Add --no-defaultroom to * prevent use of the default room and to reject calls without a room name. * * Revision 1.13 2001/03/18 07:40:45 robertj * Fixed MSVC compatibility. * * Revision 1.12 2001/03/18 06:50:20 robertj * More changes for multiple conferences from Patrick Koorevaar. * * Revision 1.11 2001/03/05 22:36:22 robertj * Changes for logging and multiple conferences from Patrick Koorevaar. * * Revision 1.10 2001/02/09 06:09:42 robertj * Added fix for crashing on exit problem, thanks Dhomin. * * Revision 1.9 2001/02/08 07:06:37 robertj * Added 'm' command to make call, thanks Paul Zaremba. * Added ability to send CIF size images, thanks again Paul Zaremba. * * Revision 1.8 2001/01/23 02:55:05 dereks * Add user interface thread to openmcu. * tidy up the exiting process, but it is still in need of work. * * Revision 1.7 2001/01/03 03:59:26 dereks * Adjusted algorithm for selecting which corners contain which video stream. * Add gsmframes and g711frames option. Add documentation describing data flows. * * Revision 1.6 2000/12/22 08:28:23 dereks * Optimise video handling, and reduce load on mcu computer * Include noise detection routine, to determine which images are displayed when > 4 connections. * * Revision 1.5 2000/12/19 22:41:44 dereks * Add video conferencing - limited to 5 connected nodes. * Use the video channel facility now in openh323 and pwlib modules * Add simple interface to handle commands entered at the keyboard. * * Revision 1.4 2000/11/02 03:33:41 craigs * Changed to provide some sort of software timeing loop * * Revision 1.3 2000/05/25 12:06:20 robertj * Added PConfigArgs class so can save program arguments to config files. * * Revision 1.2 2000/05/10 08:11:57 craigs * Fixed copyrights and names * * Revision 1.1 2000/05/10 05:54:07 craigs * Initial version * */ #ifndef _OpenMCU_MAIN_H #define _OpenMCU_MAIN_H #include #include #include #include #include #include /** ***** Data flow. ***Audio. At any point in time, there are N nodes connected. Consequently, there are N copies of MyH323Connection class, which I will label connA, connB... connN Ther is only ever one endpoint class, which is MyH323EndPoint. There are N*(N-1) instances of audiobuffers Each connection has a dictionary, containing (N-1) instances of audiobuffers. connI has audioBuffers, labelled abA, abB, abC... (not abI) ...abM, abN >> IncomingAudio (audio data arives at the mcu) a)the audio codecs write to the IncomingAudio channel b)IncomingAudio sends data to connI c)connI writes the data to the endpoint. d)the endpoint copies the data to connA, connB.. (not connI)...connM, connN e)the connections listed in step d copy the data to the specified audiobuffer. Thus, audio data from connI is copied into abI for connA, copied into abI for connB, copied into abI for connC etc. Thus, audio data from connI is copied (N-1) times. >> OutgoingAudio (the audio encoder requests audio data to send) a)the audio codec requests data from the OutgoingAudio channel b)the OutgoingAudio channel requests data from the connI c)connI requests data from the endpoint. d)the endpoint (MyH323EndPoint::ReadAudio) method then finds the connection associated with audio codec that has requested data. - in this case connI. e)The MyH323Connection::ReadAudio method is then called for connI. f)MyH323Connection::ReadAudio combines the data in each of its audiobuffers. which is abA, abB, abC... (not abI) ...abM, abN You will notice that Outgoing audio has additional work, in that connI (at step c) could bypass the endpoint and go directly to its own audiobuffers and read the data. This code is not SMP safe, because then the memberMutex does not protect access (by outgoing audio code) to the connections. *****Video is simple. There is a video buffer in the endpoint class. When an audio packet arrives, it moves the marker for that connection up a list. If a particular connection is in the top 4 (it has spoken recently), then when a video frame arrives, the connection writes the frame to the specified section of the video buffer (eg, top left corner) When video is requested, the entire frame of data in the video buffer is copied and returned to the connection. **/ class AudioBuffer : public PObject { PCLASSINFO(AudioBuffer, PObject); public: AudioBuffer(); ~AudioBuffer(); void Write(const BYTE * ptr, PINDEX amount); void Read(BYTE * ptr, PINDEX amount); void ReadAndMix(BYTE * ptr, PINDEX amount, PINDEX channels); protected: void Mix(BYTE * dst, const BYTE * src, PINDEX count, PINDEX channels); BYTE * buffer; PINDEX bufferLen; ///Number of bytes unread in the buffer. PINDEX bufferStart; ///Current position in the buffer. PINDEX bufferSize; ///Total number of bytes in buffer. Never gets changed. PMutex audioBufferMutex; }; #ifndef NO_VIDEO class VideoBuffer : public PObject { PCLASSINFO(VideoBuffer, PObject); public: VideoBuffer(); ~VideoBuffer(); void Write( BYTE * ptr, PINDEX amount, PINDEX posn); void Read(BYTE * ptr, PINDEX amount); void Clear(PINDEX posn); //Reset the buffer at the specified position void SetSize(int x, int y); // Set the vidbuffer size protected: BYTE * buffer; PINDEX videoBufferSize; ///Total number of bytes in buffer. Never gets changed. PMutex videoBufferMutex; int bufferFrameSize; int xSize, ySize; }; #endif class AudioDelay : public PObject { PCLASSINFO(AudioDelay, PObject); public: AudioDelay(); BOOL Delay(int time); void Restart(); int GetError(); protected: PTime previousTime; BOOL firstTime; int error; }; class MyH323Connection; 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 { PCLASSINFO(MyH323EndPoint, H323EndPoint); 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); virtual void TranslateTCPAddress(PIPSocket::Address &localAddr, const PIPSocket::Address &remoteAddr); BOOL behind_masq; PIPSocket::Address *masqAddressPtr; // new functions virtual void ListenForIncomingCalls(); virtual void AwaitTermination(); // delegate functions void AddMember (MyH323Connection * conn); void RemoveMember(MyH323Connection * conn); // new functions 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 ReadVideo(const PString & token, void * buffer, PINDEX amount); BOOL WriteVideo(const PString & token, const void * buffer, PINDEX amount, PString roomID); PINDEX FindTokensVideoPosn(const PString & thisToken); BOOL AddVideoPosnToken(const PString & thisToken); void SetVideoSize(int x, int y); void EnableVideoReception(BOOL isOK) { hasVideo = isOK; } #endif void HandleUserInterface(); #ifndef NO_VIDEO int videoTxQuality, videoFramesPS, videoFill; BOOL videoLarge; #endif void SetDefaultRoomName(PString roomName); PString GetDefaultRoomName(void); 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; #ifndef NO_VIDEO StringListDict spokenListDict; PString videoPosn[4]; //Describes where video data is displayed. BOOL hasVideo; VideoBuffer videoBuffer; //All connected nodes write video data into this buffer #endif PSyncPoint exitFlag; 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 { PCLASSINFO(OutgoingAudio, PChannel); public: OutgoingAudio(MyH323EndPoint & ep, MyH323Connection & conn); BOOL Read(void * buffer, PINDEX amount); BOOL Close(); protected: void CreateSilence(void * buffer, PINDEX amount); MyH323EndPoint & ep; MyH323Connection & conn; AudioDelay delay; PMutex audioChanMutex; }; class IncomingAudio : public PChannel { PCLASSINFO(IncomingAudio, PChannel); public: IncomingAudio(MyH323EndPoint & ep, MyH323Connection & conn); BOOL Write(const void * buffer, PINDEX amount); BOOL Close(); protected: MyH323EndPoint & ep; MyH323Connection & conn; PMutex audioChanMutex; AudioDelay delay; }; //////////////////////////////////////////////////// /** OutGoingVideo describes the data leaving the computer, and then sent by TCP/IP methods to the remote computer. OutGoingVideo is the connection/channel which connects the codec and the connection class, for the transport of data. */ #ifndef NO_VIDEO class OutgoingVideo : public PVideoChannel { PCLASSINFO(OutgoingVideo, PVideoChannel); public: OutgoingVideo(MyH323EndPoint & ep, MyH323Connection & conn, int framesPerSec, BOOL videoLarge); ~OutgoingVideo(); BOOL Close(); /** uses over ride of Read function in the PChannel class. */ BOOL Read(void * buffer, PINDEX amount); void SetRenderFrameSize(int /*_width*/, int /*_height*/) {PTRACE(3,"OutgoingVideo Set size");} BOOL Redraw(const BYTE * /*frame*/) { return TRUE; } BOOL IsOpen() const { return !closed; } BOOL IsGrabberOpen() { return TRUE; } PINDEX GetGrabWidth() { return (videoLarge ? 352 : 176); } /**Return the height of the currently selected grabbing device. */ PINDEX GetGrabHeight() { return (videoLarge ? 288 : 144 ); } protected: MyH323EndPoint & ep; MyH323Connection & conn; PMutex videoChanMutex; BOOL videoLarge; AudioDelay delay; int msBetweenFrames; BOOL closed; }; //////////////////////////////////////////////////// /** IncomingVideo describes the data entering the computer, which is placed in the video buffer. */ class IncomingVideo : public PVideoChannel { PCLASSINFO(IncomingVideo, PVideoChannel); public: IncomingVideo(MyH323EndPoint & ep, MyH323Connection & conn); ~IncomingVideo(); BOOL Write(const void * buffer, PINDEX amount); BOOL Close(); void SetRenderFrameSize(int _width, int _height); BOOL Redraw(const BYTE * frame) { return Write(frame,0); } BOOL IsOpen() const { return !closed; } PINDEX GetGrabWidth() { PTRACE(3,"incomingvideo get grab width"); return width; } /**Return the height of the currently selected grabbing device. */ PINDEX GetGrabHeight() { return height; } protected: MyH323EndPoint & ep; MyH323Connection & conn; PMutex videoChanMutex; PINDEX width; PINDEX height; PINDEX frameSize; AudioDelay delay; BOOL closed; }; #endif //NO_VIDEO PDICTIONARY(AudioBufferDict, PString, AudioBuffer); class MyH323Connection : public H323Connection { PCLASSINFO(MyH323Connection, H323Connection); public: MyH323Connection(MyH323EndPoint &, unsigned); ~MyH323Connection(); // overrides from H323Connection BOOL OpenAudioChannel(BOOL, unsigned, H323AudioCodec & codec); void CleanUpOnCallEnd(); #ifndef NO_VIDEO BOOL OpenVideoChannel( BOOL isEncoding, H323VideoCodec & codec); #endif 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); void RemoveMember(const PString & token); // functions called for incoming and outgoing channels. // These provide a link between the audio/video channels and the endpoint. BOOL OnIncomingAudio(const void * buffer, PINDEX amount); BOOL OnOutgoingAudio(void * buffer, PINDEX amount); #ifndef NO_VIDEO BOOL OnIncomingVideo(const void * buffer, PINDEX amount); BOOL OnOutgoingVideo(void * buffer, PINDEX & amount); #endif // functions called to put data in/out of the buffer array. BOOL ReadAudio(const PString & token, void * buffer, PINDEX amount); BOOL WriteAudio(const PString & token, const void * buffer, PINDEX amount); #ifndef NO_VIDEO void EnableVideoReception(BOOL isOK) { hasVideo = isOK; } PString GetVideoTransmitCodecName() const { return videoTransmitCodecName; } PString GetVideoReceiveCodecName() const { return videoReceiveCodecName; } #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; IncomingAudio * incomingAudio; OutgoingAudio * outgoingAudio; PMutex videoMutex; BOOL connected; BOOL roomMuted; BOOL userMuted; BOOL roomOwner; #ifndef NO_VIDEO PString videoTransmitCodecName, videoReceiveCodecName; BOOL hasVideo; IncomingVideo * incomingVideo; OutgoingVideo * outgoingVideo; #endif 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); public: UserInterfaceThread(MyH323EndPoint & end) : PThread(1000, NoAutoDeleteThread), endpoint(end) { Resume(); } void Main() { endpoint.HandleUserInterface(); } protected: MyH323EndPoint & endpoint; }; #endif // End of File ///////////////////////////////////////////////////////////////