/*
 * h323caps.cxx
 *
 * H.323 protocol handler
 *
 * Open H323 Library
 *
 * Copyright (c) 1998-2000 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 Open H323 Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Portions of this code were written with the assisance of funding from
 * Vovida Networks, Inc. http://www.vovida.com.
 *
 * Contributor(s): ______________________________________.
 *
 * Revision 1.48.2.4  2015/05/04 15:42:12  rich 
 * Let the Tandberg MXP do 720P decode  
 *
 * $Log: h323caps.cxx,v $
 * Revision 1.55  2016/02/25 00:07:23  joseph
 * VGW-1938 - implement FECC for H.323
 * -added H235 encryption for inbound FECC
 * -removed some warnings
 *
 * Revision 1.54  2016/01/25 20:19:20  roni
 * remove inlining of GetFormatName to prevent crash
 *
 * Revision 1.53  2016/01/22 20:46:00  roni
 * remove pure virtual attribute and add base class implementation to prevent crash
 *
 * Revision 1.52  2015/06/24 20:34:32  rich
 * Add MXP flag to force 720P on MXP endpoints
 *
 * Revision 1.51  2015/05/07 19:00:36  vfurman
 * merged from BRANCH_GW_3_2 (tag:	MERGE_GW_3_2_TO_TIP_2015_05_07)
 * Revision 1.48.2.5  2015/05/12 21:15:09  rich
 * Add MXP flag to only enable 720P from config
 *
 * Revision 1.48.2.4  2015/05/04 15:42:12  rich
 * Let the Tandberg MXP do 720P decode
 *
 * Revision 1.50  2014/11/03 21:20:34  joseph
 * Fixed H.239 issue that caused a black on the Sony Sony endpoint (Merged from 3.2)
 * Revision 1.48.2.3  2014/12/04 13:59:00  joseph
 * Fixed Jira Items VGW-811 and VGW-816.
 *
 * Revision 1.48.2.2  2014/10/30 16:50:38  joseph
 * Fixed H.239 issue that caused a black on the Sony Sony endpoint (changes reviewed by Roni and Rich).
 * - Modified the H323 stack to store "maxMBPS" in the media format object such that it is available from ep.cxx.
 * - Modified ep.cxx such that the width and height for a channel is retrieved from the stack instead of calling GetRemoteMaxCapabilities() directly from ep.cxx
 *
 * Revision 1.48.2.1  2014/10/15 13:02:14  rich
 * VGW-701 VidyoGateway does not open connection at H.264 720p when connecting with Avaya Scopia / Radvision XT5000 endpoint. instead of 1280x720p it opens 1104x720p
 *         Fixed maxFS calculation when the endpoint sets it to 0
 *
 * Revision 1.48  2014/08/03 19:42:35  rich
 * Change ptrace log level to 3 from 4 to get more details of the caps negotiation
 *
 * Revision 1.47  2014/06/16 18:44:52  rich
 * VGW-113 Issues with sharing to/from Polycom RMX 2000
 *         Create gwconfig flag (oldRmx) that is used to apply RMX specific code
 *         Default is oldRmx=1 so the behavior is unchanged
 *         We can create a .vidyo file to enable/disable the feature.
 *         Later we can set the default to 0 if we find it is no longer needed
 *
 * Revision 1.46  2014/05/16 03:09:10  rich
 * VGW-236  Content sharing not working between VidyoGW and RadVision Scopia
 *          Issue was maxMbps was assuming 30 fps where app share is at 5 fps
 *
 * Revision 1.45  2014/05/12 22:10:09  roni
 * merge from 3.1
 *
 * Revision 1.42.2.6  2014/06/15 17:46:07  rich
 * VGW-113 Issues with sharing to/from Polycom RMX 2000
 *         Create gwconfig flag (oldRmx) that is used to apply RMX specific code
 *         Default is oldRmx=1 so the behavior is unchanged
 *         We can create a .vidyo file to enable/disable the feature.
 *         Later we can set the default to 0 if we find it is no longer needed
 *
 * Revision 1.42.2.5  2014/05/13 19:55:29  rich
 * VGW-236  Content sharing not working between VidyoGW and RadVision Scopia
 *          Issue was maxMbps was assuming 30 fps where app share is at 5 fps
 *
 * Revision 1.42.2.4  2014/04/18 12:15:01  roni
 * Jira item VGW-43 - VidyoGateway calls to the RadVision Scopia are failing
 *
 * Revision 1.42.2.3  2014/04/14 17:37:42  roni
 * *** empty log message ***
 *
 * Revision 1.42.2.2  2014/04/09 00:48:23  rich
 * VGW-70 For 1080P H.323 call, gateway is 720P resolution.
 *
 * Revision 1.42.2.1  2014/01/30 14:01:22  roni
 * fix h264 caps to resolution conversion logic
 *
 * Revision 1.42  2014/01/24 19:30:21  rich
 * Move HEAD to match TAG_GW_3_0_0_98 before branching for 3_1 release
 *
 * Revision 1.41.2.2  2013/12/04 03:11:16  rich
 * default is sqcif@15 64kbps
 *
 * Revision 1.41.2.1  2013/08/16 15:15:11  rich
 * merged TAG_GW_2_2_1_MergePoint_9 == TAG_GW_2_2_1_99
 *
 * Revision 1.41  2013/07/25 13:36:47  rich
 * Add comments to match merge points
 *
 * Revision 1.40  2013/07/25 12:46:02  rich
 * Make H264 preference for H.239 - TAG_GW_2_2_1_MergePoint_8
 *
 * Revision 1.39  2013/07/03 18:52:45  vfurman
 * data_tos +
 *
 * Revision 1.38  2013/06/14 21:25:47  roni
 * Fixed BlueJeans issue.  The H323 stack was selecting a High profile capability.
 *
 * Revision 1.37  2013/05/15 15:06:48  joseph
 * Issue fixed at ITMC 2013.  The GW supports only H.264 baseline profile.  Ignore all remote H.264 caps that do not specify the "baseline" profile.
 *
 * Revision 1.36  2013/04/08 13:37:11  roni
 * merge from 2.2.1
 *
 * Revision 1.35.2.1  2013/04/01 17:19:31  joseph
 * Fixed H.239 issue with the New SVC Polycom endpoint
 * In additon, restructured H.239 and FECC processing to reduce the dependency on the connection object. Removed dead VCAP FECC code.
 *
 * Revision 1.35  2012/09/27 14:11:36  rich
 * Match legacy resolution on all calls
 *
 * Revision 1.35.2.7  2013/08/12 18:23:13  joseph
 * Fix NTT issue - Remove the H264 "high" profile filter from the H323 stack.  This was causing a problem with Arkadin's RMX 1500 MCU.  With the "high" profile filter, the GW was selecting the base profile H264 cap.  As a result, the RMX was issuing a new TCS with lower h264 levels.  Since the GW does not support H323 renegotation, the h239 shares were failing.  Hopefully this fixes the issue.
 *
 * Revision 1.32.2.2  2012/08/24 16:42:26  joseph
 * Checking in Roni's fix for the H264 SD VSX6000 issue - basically lock down to CIF resolution when the legacy endpoint advertises a CIF H264 base level with a frame rate of 15 or less (this would be levels 1.1 and 1.2).
 *
 * Revision 1.32.2.1  2012-05-24 19:01:44  joseph
 * H239 role value fix found at IMTC
 *
 * Revision 1.32  2012-03-07 01:04:19  joseph
 * Initial checking for legacy H264-SVC support using H264-AVC packetization.
 *
 * Revision 1.31  2012-01-06 00:59:46  joseph
 * Fix encryption with Polycom VSX6000 endpoint.  If this endpoint had "optional" encryption and the service prefix was H235OFF, the GW was encrypting the to-legacy media channels.  This fix resolves this issue.
 *
 * Revision 1.30  2011-10-25 20:58:34  joseph
 * Enable QCIF when BW is below 128K
 *
 * Revision 1.29  2011-10-19 22:31:50  joseph
 * New FECC support (plus removed some printfs)
 *
 * Revision 1.28  2011-07-13 01:39:15  achen
 * Add 1080P support
 *
 * Revision 1.27  2011-06-16 16:43:46  joseph
 * Fixed a G711 audio issue with NEC endpoint (for Tanaka-san).  The fix is to set the sampling rate to "20" in the G711 caps that the GW advertises.
 *
 * Revision 1.26  2011-05-10 03:14:26  joseph
 * Added FECC (Far End Camera Control) feature to the GW.
 *
 * Revision 1.25  2011-03-14 04:52:51  joseph
 * Made the following changes:
 * 1) H.235 is configurable on a per prefix basis (ON, OFF, OPTIONAL).  There is a global system wide H.235 option which can be enabled/disabled via a CLI command. When enabled, this H.235 system option overides the H.235 service prefix options and will require H.235 for all GW calls.  When disabled (the default), H.235 will be controlled by the H.235 setting in the service prefix.
 * 2) H263 changes - The H263 plugin will allow a dynamic payloadtype for encrypting H263 streams to endpoints that do not support RFC2429 (i.e. H263+) packetization.
 * 3) Moved the H.239 trigger for "Codian VCR" endpoints to wavchan.cxx (when the first RTP audio packet arrives from the endpoint).  The Codian VCR endpoint opens only an audio OLC.  For all other endpoints this trigger happens when the first RTP video packet arrives from the endpoints.  This mechanism fails for the Codian VCR because, as a recorder, it does not open a video OLC and the H.239 presentations don't work.
 *
 * Revision 1.24  2011-03-07 23:12:55  rich
 * Merge all changes up to TAG_GW_2_0_4_233
 *
 * Revision 1.19.2.10.6.1.6.6  2011-03-03 04:36:51  joseph
 * Fixed G722 audio issue with RMX 2000.  This issue was introduced by recent restructing changes for H.235.
 *
 * Revision 1.19.2.10.6.1.6.5  2011-03-02 03:36:14  joseph
 * Fixed encrypted G722 audio issue for Tandberg endpoints.  These endpoints use dynamic rtp payloadtypes (96-127) for audio codecs.  The GW will use payloadtype 127 for G722 audio streams to Tandberg endpoints.
 * The GW will remove the G711 codec for all secure (H235) calls to Tandberg endpoints.  This restriction will be lifted when we can figure out how to set the G711 codec to use a dynamic payload type.
 *
 * Revision 1.19.2.10.6.1.6.4  2011-02-26 01:55:45  joseph
 * Made the following modifications:
 * 1) Correted the terminal capabilities descriptors table to group the caps into 3 groups, 1 audio group, 1 video group and one H239 group. This fixed the G722 issue with the Polycom endpoints.
 * 2) Shifted the H235 caps to be appended at the end of the terminal capabilities table.
 * 3) Removed the G722 restriction on the polycom endpoints (fixed as a side effect of #1 above)
 * 4) Fixed the H.239 issue for recorder endpoints (issue from Google)
 * 5) Fixed Flow control problem.  The raw bit rate from the H323 message had to be multiplied by 100 so that it would in order of 1000k (i.e adjust 3200 to 320000 so that it becomes 320k).
 *
 * Revision 1.19.2.10.6.1.6.3  2011-02-22 17:11:18  joseph
 * OTN changes.
 *
 * Revision 1.19.2.10.6.1.6.2  2011-02-09 00:25:08  joseph
 * Fixed two issues:
 * 1) H263 depacketization issue - For H263 video stream, use the first packet in the stream to determine the media packetization (RFC2190/RFC2429) used by the remote endpoint.  Use that media packetization type for the lifetime of the H263 decoder.
 * 2) H239 signalling issue for secure calls (aka H235). For secure calls, the GW was rejecting the H263 extended OLC requests.
 *
 * Revision 1.19.2.10.6.1.6.1  2011-01-20 01:01:01  joseph
 * Changes for H.235 media encryption.
 *
 * Revision 1.22  2010-12-03 17:54:10  roni
 * merge changes from branch 2.0.4.192
 *
 * Revision 1.19.2.10.6.1  2010-11-20 01:46:54  joseph
 * Removed the "role" check for the extended video capabilities issues by the Aethra endpoints.
 *
 * Revision 1.19.2.10  2010-07-27 00:07:49  joseph
 * Removed the XGA (1024x768) encode resolution cap for H.239 legacy presentation streams.  This applies to both codes (H263 and H264).  The GW will now encode H.239 presentations based on the maximum decode capabilities stated by the endpoint in its extended video caps.  The Gateway however, will continue to advertise a max H.239 decode resolution of XGA.
 *
 * Revision 1.19.2.9  2010-07-23 03:11:01  joseph
 * Resolved MR 4908 - include the remote endpoint's "h263options" in the H263 open logical channel for H239 presentations.
 *
 * Revision 1.19.2.8  2010-07-12 20:46:40  joseph
 * Support QCIF resolutions for inbound SIP and H323 calls. Also do not advertise G711 when selecting G722 as the audio codec
 *
 * Revision 1.19.2.7  2010-07-10 00:01:19  joseph
 * Override H264 bit rate defined by the H264 level.  Use the maxBitRate set in the H264 capability.
 *
 * Revision 1.19.2.6  2010-07-09 20:12:06  joseph
 * Override H264level bit rate with the capability's bit rate.
 *
 * Revision 1.19.2.5  2010-06-23 00:14:57  joseph
 * Resolved issues found at the IMTC SuperOp event.
 * Specifically addressed the following issues:
 * 1) Fixed registration issue with TB GK.  Make unique alias names for each GW.  This is required by HP
 * 2) Fixed G722 issue with RMX-2000
 * 3) Fixed H.239 issue with new Scopia Radvision endpoint.
 *
 * Revision 1.19.2.4  2010-06-03 00:01:08  joseph
 * Fixed MR 4562 - This is the issue where the GW was streaming 4CIF for a CIF call.
 *
 * Revision 1.19.2.3  2010-05-22 00:16:01  joseph
 * Fixed H.239 issues with RMX, "Sony PCS-1" and other H.263 issues.
 *
 * Revision 1.19.2.2  2010-04-29 23:26:44  joseph
 * Fixed two issues:
 * 1) MR 4433 - allow RMX to work in H.264 with SD and HD resolutions. (H263 remains an issue)
 * 2) allow the order preference of codecs specified in the service profile (i.e. H264_H263 for video and G711_G722 for audio)
 *
 * Revision 1.19.2.1  2010-04-20 18:34:47  joseph
 * H.239 Rate Control and Flow Control null callback
 *
 * Revision 1.19  2010-03-31 18:52:04  joseph
 * Made additional modifications to ensure that the bandwitdh is correctly allocated and signalled for both incoming and outoing calls for both codes and for both main and extended channels.
 *
 * Revision 1.18  2010-03-24 00:28:41  joseph
 * Changes to allow GW to interoperate with the Polycom MGC.
 *
 * Revision 1.17  2010-03-09 02:50:45  joseph
 * Pass the remote endpoint's level in the unused "Generic Parameter 0" parameter.
 *
 * Revision 1.16  2010-03-06 01:40:18  joseph
 * Allow the "level" paramater (generic parameter 42) to become visible to the h264 plugin.
 *
 * Revision 1.15  2010-03-05 01:35:29  joseph
 * More changes for caps merge to resolve a Tandberg issue and Polycom PCS1 issue.
 *
 * Revision 1.14  2010-03-04 01:38:38  joseph
 * Fixed issue with Tandberg endpoint.
 *
 * Revision 1.13  2010-03-03 18:55:06  roni
 * *** empty log message ***
 *
 * Revision 1.12  2010-03-03 17:36:10  roni
 * ignore tandberg rcdo
 *
 * Revision 1.11  2010-03-03 01:17:48  joseph
 * Resolved video issues for the following endpoints: "Sony PCS-1" and "Sony PCS-XG80".  The PCS-1 endpoint was getting "black" video.  The GW was not encoding 704x576 for the SD video calls.
 *
 * Revision 1.10  2010-03-02 01:23:21  joseph
 * Fixed MR 4151.  In ep.cxx narrowed scoped of H.264* to H.264-CIF, H.264-SD and H.264-HD, and H.263* to H.263-CIF, H.263-SD.  The problem with the H.263* and H.264* is that they would also include the extended video caps.
 *
 * Revision 1.9  2010-02-24 01:14:23  joseph
 * Fixed bandwidth negotiations needed for the MGC.
 *
 * Revision 1.8  2010-02-19 01:06:35  joseph
 * - Added H.239 support for RMX
 * - Don't stop a legacy's inbound H.239 share when adding a Vidyo share (reported by Rich from Videra customer sight)
 * - Modified the following files:
 * callbacks.cxx - don't stop inbound legacy shares when adding a Vidyo share
 * ep.cxx - wait for tokenresponse before issuing an H.239 extended open logical channel. Also remove inbound share from VD when the inbound channel becomes inactive. These changes were needed for the RMX
 * h264.cxx - added MaxMBPS, MaxFS and MaxDPB parameters to the H.239-H264 codec plugin definition.  The RMX requires these parameters to grant an inbound H.239 presentation.
 * h323caps.cxx - when issuing an extended open logical channel request, copy the profile level of the remote endpoint (instead of using the HD level used in the H.239 plugin definition.
 *
 * Revision 1.7  2010-02-18 01:07:43  joseph
 * Fixed a problem with overriding the MaxBitRateOption parameter in the stack.
 *
 * Revision 1.6  2010-02-17 19:27:15  roni
 * pass remote max bit rate to plugin
 *
 * Revision 1.5  2010-02-15 19:02:20  roni
 * merge BRANCH_GW_2_0_0_65_H239 into tip
 *
 * Revision 1.3.2.2  2010-02-10 01:18:05  joseph
 * Merge of TAG_GW2_H239_MERGE_4 and TAG_GW2_H239_MERGE_5
 *
 * Revision 1.3.2.1  2010-01-21 21:46:44  joseph
 * First merge of the H239 functionality.
 *
 * Revision 1.3  2009-12-11 20:02:01  roni
 * *** empty log message ***
 *
 * Revision 1.6.2.1  2009-12-05 01:26:30  joseph
 * Scopia changes.  Modified the ordering of the H264 optional parameters in both the open_logical_channel and in the terminal_capability_set.  In both
 * cases the mandatory Profile and Level parameters should be the listed first (ahead of the optional parameters).
 *
 * Revision 1.6  2009-06-08 18:45:54  roni
 * make exact copy of remote H264 cap for openlogicalchannel message
 *
 * Revision 1.5  2009/03/13 18:40:42  roni
 * *** empty log message ***
 *
 * Revision 1.4  2009/02/03 15:16:20  roni
 * *** empty log message ***
 *
 * Revision 1.3  2009/02/03 13:53:10  roni
 * copy max MB/sec parameter from remote h.245 capability to remote h.323 capability
 *
 * Revision 1.2  2009/01/29 20:35:24  roni
 * *** empty log message ***
 *
 * Revision 1.1.1.1  2009/01/02 16:23:16  roni
 * adding openH323
 *
 * Revision 1.4  2008/12/22 15:30:13  roni
 * *** empty log message ***
 *
 * Revision 1.3  2008/11/10 19:50:40  roni
 * fix H.264 neg to allow for RCDO
 *
 * Revision 1.2  2008/10/23 19:59:12  roni
 * *** empty log message ***
 *
 * Revision 1.15  2008/01/30 02:09:38  shorne
 * Fixes for removing and reloading extended Video Capability
 *
 * Revision 1.14  2007/12/07 07:33:22  shorne
 * Settled on codecs greater than CIF to be generic
 *
 * Revision 1.13  2007/11/20 11:40:47  willamowius
 * fix compilation without audio support Thanks Vladimir Voronin
 *
 * Revision 1.12  2007/11/14 08:55:22  shorne
 * Added ability to set DSCP values for audio/video
 *
 * Revision 1.11  2007/11/10 13:37:32  shorne
 * Corrected Conference Control capability type and resolved error with PTRACE on release build
 *
 * Revision 1.10  2007/11/06 17:43:36  shorne
 * added i480 standard framesize
 *
 * Revision 1.9  2007/11/01 20:17:33  shorne
 * updates for H.239 support
 *
 * Revision 1.8  2007/10/30 04:23:45  shorne
 * Corrections and Improvements for H.239 support
 *
 * Revision 1.7  2007/10/25 21:08:04  shorne
 * Added support for HD Video devices
 *
 * Revision 1.6  2007/10/19 19:54:17  shorne
 * ported latest Video updates in OpenH323 committed after h323plus initial fork thanks
 *  Robert
 *
 * Revision 1.5  2007/10/16 17:00:28  shorne
 * Added H.230 Support
 *
 * Revision 1.4  2007/08/20 20:19:52  shorne
 * Moved opalplugin.h to codec directory to be plugin compile compatible with Opal
 *
 * Revision 1.3  2007/08/20 19:13:28  shorne
 * Added Generic Capability support. Fixed Linux compile errors
 *
 * Revision 1.2  2007/08/08 20:13:29  shorne
 * Fix H.249 typo
 *
 * Revision 1.1  2007/08/06 20:51:06  shorne
 * First commit of h323plus
 *
 * Revision 1.105.2.10  2007/09/05 04:14:40  rjongbloed
 * Back ported from OPAL media packetization in TCS
 *
 * Revision 1.105.2.5  2007/07/19 20:05:05  shorne
 * Added QoS as default
 *
 * Revision 1.105.2.4  2007/03/24 23:39:43  shorne
 * More H.239 work
 *
 * Revision 1.105.2.3  2007/02/19 20:11:05  shorne
 * Added Baseline H.239 Support
 *
 * Revision 1.105.2.2  2007/02/18 17:11:23  shorne
 * Added H.249 Extended UserInput Support
 *
 * Revision 1.105.2.1  2007/02/02 22:12:43  shorne
 * Added ability to set FrameSize for video plugins
 *
 * Revision 1.105  2006/10/10 04:21:56  csoutheren
 * Fixed compile problem on Linux with latest PWLib
 *
 * Revision 1.104  2006/09/05 23:56:57  csoutheren
 * Convert media format and capability factories to use std::string
 *
 * Revision 1.103  2006/05/16 11:36:01  shorne
 * added AEC support and  H323VdieoCapability default constructor
 *
 * Revision 1.102  2005/06/07 07:09:13  csoutheren
 * Removed compiler warnings on Windows
 *
 * Revision 1.101  2005/06/07 03:22:24  csoutheren
 * Added patch 1198741 with support for plugin codecs with generic capabilities
 * Added patch 1198754 with support for setting quality level on audio codecs
 * Added patch 1198760 with GSM-AMR codec support
 * Many thanks to Richard van der Hoff for his work
 *
 * Revision 1.100  2005/01/03 06:25:55  csoutheren
 * Added extensive support for disabling code modules at compile time
 *
 * Revision 1.99  2004/12/08 02:14:26  csoutheren
 * Guard against NULL capabilities
 *
 * Revision 1.98  2004/08/26 08:05:04  csoutheren
 * Codecs now appear in abstract factory system
 * Fixed Windows factory bootstrap system (again)
 *
 * Revision 1.97  2004/08/24 14:23:11  csoutheren
 * Fixed problem with plugin codecs using capability compare functions
 *
 * Revision 1.96  2004/07/07 03:52:12  csoutheren
 * Fixed incorrect strings returned by GetFormatName on G.711 codecs
 *
 * Revision 1.95  2004/07/03 07:05:42  csoutheren
 * Applied patch 979639 to prevent crash in FindCapability if capability does not exist
 * Thanks to Dave Parr
 *
 * Revision 1.94  2004/06/30 12:31:16  rjongbloed
 * Rewrite of plug in system to use single global variable for all factories to avoid all sorts
 *   of issues with startup orders and Windows DLL multiple instances.
 *
 * Revision 1.93  2004/06/17 22:40:56  csoutheren
 * Fixed typo in FindCapability, thanks to Vyacheslav Frolov
 *
 * Revision 1.92  2004/06/16 07:50:00  csoutheren
 * Fixed potential crash caused when user indication capabilities are not present. Thanks to Yuri Kiryanov
 *
 * Revision 1.91  2004/06/08 13:32:18  rjongbloed
 * Fixed (pre)condition for checking and matching capabilities, thanks Guilhem Tardy
 *
 * Revision 1.90  2004/06/08 01:20:28  csoutheren
 * Provide backwards compatibility for applications using old capability API
 *
 * Revision 1.89  2004/06/03 13:32:00  csoutheren
 * Renamed INSTANTIATE_FACTORY
 *
 * Revision 1.88  2004/06/03 12:48:35  csoutheren
 * Decomposed PFactory declarations to hopefully avoid problems with DLLs
 *
 * Revision 1.87  2004/06/01 05:48:03  csoutheren
 * Changed capability table to use abstract factory routines rather than internal linked list
 *
 * Revision 1.86  2004/05/31 01:32:24  csoutheren
 * Removed debugging left behind
 *
 * Revision 1.85  2004/05/26 04:13:21  csoutheren
 * Safeguard against accidentally removing all capabilities by passing empty string to RemoveCapability
 *
 * Revision 1.84  2004/05/26 04:00:49  csoutheren
 * Fixed calling of non-standard capability compare function used primarily for plugin codecs
 *
 * Revision 1.83  2004/05/20 02:07:29  csoutheren
 * Use macro to work around MSVC internal compiler errors
 *
 * Revision 1.82  2004/05/19 23:42:48  csoutheren
 * Included <algorithm>
 *
 * Revision 1.81  2004/05/19 13:28:30  csoutheren
 * Changed H323EndPoint::AddAllCapabilities to only add standard media formats
 *
 * Revision 1.80  2004/05/19 07:38:23  csoutheren
 * Changed OpalMediaFormat handling to use abstract factory method functions
 *
 * Revision 1.79  2004/05/13 12:49:33  rjongbloed
 * Fixed usage of the compare function in non-standard capabilities.
 *
 * Revision 1.78  2004/05/10 01:01:25  csoutheren
 * Guarded against NULL capabilities passed to AddCapability
 *
 * Revision 1.77  2004/05/06 12:53:09  rjongbloed
 * Fixed ability to encode rxtx and tx capabilities, thanks Vyacheslav Andrejev.
 *
 * Revision 1.76  2004/05/04 03:33:33  csoutheren
 * Added guards against comparing certain kinds of Capabilities
 *
 * Revision 1.75  2004/04/22 12:38:03  rjongbloed
 * Removed the RTP QoS class if there is no QoS support in build,
 *   thanks Nick Hoath, ISDN Communications Ltd.
 *
 * Revision 1.74  2004/04/21 04:58:27  csoutheren
 * Fixed problem with mode select, thanks to Vamsi K Pottangi
 *
 * Revision 1.73  2004/04/09 13:28:38  rjongbloed
 * Fixed conversion of plug ins from OpenH323 to OPAL naming convention.
 *
 * Revision 1.72  2004/04/03 10:38:25  csoutheren
 * Added in initial cut at codec plugin code. Branches are for wimps :)
 *
 * Revision 1.71  2004/04/03 08:28:06  csoutheren
 * Remove pseudo-RTTI and replaced with real RTTI
 *
 * Revision 1.70  2004/03/24 01:57:20  csoutheren
 * Updated for changes in H245v9
 *
 * Revision 1.69  2004/03/02 11:48:17  rjongbloed
 * Fixed correct capability table matching when FindCapability with a mode change,
 *   uses different enum so needs translation table.
 *
 * Revision 1.68  2003/11/08 03:11:29  rjongbloed
 * Fixed failure to call ancestor in copy ctor, thanks  Victor Ivashin.
 *
 * Revision 1.67  2003/10/27 06:03:39  csoutheren
 * Added support for QoS
 *   Thanks to Henry Harrison of AliceStreet
 *
 * Revision 1.66  2003/06/06 02:13:48  rjongbloed
 * Changed non-standard capability semantics so can use C style strings as
 *   the embedded data block (ie automatically call strlen)
 *
 * Revision 1.65  2003/05/16 07:30:20  rjongbloed
 * Fixed correct matching of OLC data types to capabilities, for example CIF
 *   and QCIF video are different and should match exactly.
 *
 * Revision 1.64  2003/04/28 07:00:09  robertj
 * Fixed problem with compiler(s) not correctly initialising static globals
 *
 * Revision 1.63  2003/04/27 23:50:38  craigs
 * Made list of registered codecs available outside h323caps.cxx
 *
 * Revision 1.62  2003/03/18 05:11:22  robertj
 * Fixed OID based non-standard capabilities, thanks Philippe Massicotte
 *
 * Revision 1.61  2002/12/05 12:29:31  rogerh
 * Add non standard codec identifier for Xiph.org
 *
 * Revision 1.60  2002/11/27 11:47:09  robertj
 * Fixed GNU warning
 *
 * Revision 1.59  2002/11/26 22:48:18  craigs
 * Changed nonStd codec identification to use a table for MS codecs
 *
 * Revision 1.58  2002/11/26 13:52:59  craigs
 * Added PrintOn function for outputting names of nonStandard codecs
 *
 * Revision 1.57  2002/11/09 04:44:24  robertj
 * Fixed function to add capabilities to return correct table entry index.
 * Cosmetic changes.
 *
 * Revision 1.56  2002/08/05 10:03:47  robertj
 * Cosmetic changes to normalise the usage of pragma interface/implementation.
 *
 * Revision 1.55  2002/07/18 08:28:52  robertj
 * Adjusted some trace log levels
 *
 * Revision 1.54  2002/06/04 07:16:14  robertj
 * Fixed user indications (DTMF) not working on some endpoints which indicated
 *   receiveAndTransmitUserInputCapability in TCS.
 *
 * Revision 1.53  2002/05/29 03:55:21  robertj
 * Added protocol version number checking infrastructure, primarily to improve
 *   interoperability with stacks that are unforgiving of new features.
 *
 * Revision 1.52  2002/05/14 23:20:03  robertj
 * Fixed incorrect comparison in non-standard capability, tnanks Vyacheslav Frolov
 *
 * Revision 1.51  2002/05/10 05:45:41  robertj
 * Added the max bit rate field to the data channel capability class.
 *
 * Revision 1.50  2002/03/26 05:51:12  robertj
 * Forced RFC2833 to payload type 101 as some IOS's go nuts otherwise.
 *
 * Revision 1.49  2002/03/05 06:18:46  robertj
 * Fixed problem with some custom local capabilities not being used in getting
 *   remote capability list, especially in things like OpenAM.
 *
 * Revision 1.48  2002/02/25 04:38:42  robertj
 * Fixed wildcard lookup with * at end of string.
 * Fixed being able to create remote capability table before have local table.
 * Fixed using add all with wildcards adding capability multiple times.
 *
 * Revision 1.47  2002/02/14 07:15:15  robertj
 * Fixed problem with creation of remoteCapabilities and the "set" part contianing
 *   pointers to objects that have been deleted. Does not seem to be a practical
 *   problem but certainly needs fixing!
 *
 * Revision 1.46  2002/01/22 07:08:26  robertj
 * Added IllegalPayloadType enum as need marker for none set
 *   and MaxPayloadType is a legal value.
 *
 * Revision 1.45  2002/01/22 06:07:35  robertj
 * Moved payload type to ancestor so any capability can adjust it on logical channel.
 *
 * Revision 1.44  2002/01/17 07:05:03  robertj
 * Added support for RFC2833 embedded DTMF in the RTP stream.
 *
 * Revision 1.43  2002/01/16 05:38:04  robertj
 * Added missing mode change functions on non standard capabilities.
 *
 * Revision 1.42  2002/01/10 05:13:54  robertj
 * Added support for external RTP stacks, thanks NuMind Software Systems.
 *
 * Revision 1.41  2002/01/09 00:21:39  robertj
 * Changes to support outgoing H.245 RequstModeChange.
 *
 * Revision 1.40  2001/12/22 01:44:30  robertj
 * Added more support for H.245 RequestMode operation.
 *
 * Revision 1.39  2001/09/21 02:52:56  robertj
 * Added default implementation for PDU encode/decode for codecs
 *   that have simple integer as frames per packet.
 *
 * Revision 1.38  2001/09/11 10:21:42  robertj
 * Added direction field to capabilities, thanks Nick Hoath.
 *
 * Revision 1.37  2001/08/06 03:08:56  robertj
 * Fission of h323.h to h323ep.h & h323con.h, h323.h now just includes files.
 *
 * Revision 1.36  2001/07/19 09:50:30  robertj
 * Added code for default session ID on data channel being three.
 *
 * Revision 1.35  2001/07/17 04:44:31  robertj
 * Partial implementation of T.120 and T.38 logical channels.
 *
 * Revision 1.34  2001/06/29 04:58:57  robertj
 * Added wildcard character '*' to capability name string searches.
 *
 * Revision 1.33  2001/06/15 16:10:19  rogerh
 * Fix the "capabilities are the same" assertion
 *
 * Revision 1.32  2001/05/31 06:28:37  robertj
 * Made sure capability descriptors alternate capability sets are in the same
 *   order as the capability table when doing reorder. This improves compatibility
 *   with endpoints that select the first capability in that list rather than the table.
 *
 * Revision 1.31  2001/05/14 05:56:28  robertj
 * Added H323 capability registration system so can add capabilities by
 *   string name instead of having to instantiate explicit classes.
 *
 * Revision 1.30  2001/05/02 16:22:21  rogerh
 * Add IsAllow() for a single capability to check if it is in the
 * capabilities set. This fixes the bug where OpenH323 would accept
 * incoming H261 video even when told not to accept it.
 *
 * Revision 1.29  2001/04/12 03:22:44  robertj
 * Fixed fast start checking of returned OLC frame count to use minimum
 *   of user setting and remotes maximum limitation, Was always just
 *   sending whatever the remote said it could do.
 *
 * Revision 1.28  2001/03/16 23:00:22  robertj
 * Improved validation of codec selection against capability set, thanks Chris Purvis.
 *
 * Revision 1.27  2001/03/06 04:44:47  robertj
 * Fixed problem where could send capability set twice. This should not be
 *   a problem except when talking to another broken stack, eg Cisco routers.
 *
 * Revision 1.26  2001/02/09 05:13:55  craigs
 * Added pragma implementation to (hopefully) reduce the executable image size
 * under Linux
 *
 * Revision 1.25  2001/01/25 07:27:16  robertj
 * Major changes to add more flexible OpalMediaFormat class to normalise
 *   all information about media types, especially codecs.
 *
 * Revision 1.24  2001/01/16 03:14:01  craigs
 * Changed nonstanard capability Compare functions to not assert
 * if compared to other capability types
 *
 * Revision 1.23  2001/01/09 23:05:24  robertj
 * Fixed inability to have 2 non standard codecs in capability table.
 *
 * Revision 1.22  2001/01/02 07:50:46  robertj
 * Fixed inclusion of arrays (with bad size) in TCS=0 pdu, thanks Yura Aksyonov.
 *
 * Revision 1.21  2000/12/19 22:32:26  dereks
 * Removed MSVC warning about unused parameter
 *
 * Revision 1.20  2000/11/08 04:50:22  craigs
 * Changed capability reorder function to reorder all capabilities matching
 * preferred order, rather than just the first
 *
 * Revision 1.19  2000/10/16 08:50:08  robertj
 * Added single function to add all UserInput capability types.
 *
 * Revision 1.18  2000/10/13 03:43:29  robertj
 * Added clamping to avoid ever setting incorrect tx frame count.
 *
 * Revision 1.17  2000/10/13 02:20:32  robertj
 * Fixed capability clone so gets all fields including those in ancestor.
 *
 * Revision 1.16  2000/08/23 14:27:04  craigs
 * Added prototype support for Microsoft GSM codec
 *
 * Revision 1.15  2000/07/13 12:30:46  robertj
 * Fixed problems with fast start frames per packet adjustment.
 *
 * Revision 1.14  2000/07/12 10:25:37  robertj
 * Renamed all codecs so obvious whether software or hardware.
 *
 * Revision 1.13  2000/07/10 16:03:02  robertj
 * Started fixing capability set merging, still more to do.
 *
 * Revision 1.12  2000/07/04 01:16:49  robertj
 * Added check for capability allowed in "combinations" set, still needs more done yet.
 *
 * Revision 1.11  2000/07/02 14:08:43  craigs
 * Fixed problem with removing capabilities based on wildcard
 *
 * Revision 1.10  2000/06/03 03:16:39  robertj
 * Fixed using the wrong capability table (should be connections) for some operations.
 *
 * Revision 1.9  2000/05/30 06:53:48  robertj
 * Fixed bug where capability numbers in duplicate table are not identical (should be!).
 *
 * Revision 1.8  2000/05/23 11:32:37  robertj
 * Rewrite of capability table to combine 2 structures into one and move functionality into that class
 *    allowing some normalisation of usage across several applications.
 * Changed H323Connection so gets a copy of capabilities instead of using endponts, allows adjustments
 *    to be done depending on the remote client application.
 *
 * Revision 1.7  2000/05/10 04:05:34  robertj
 * Changed capabilities so has a function to get name of codec, instead of relying on PrintOn.
 *
 * Revision 1.6  2000/05/02 04:32:26  robertj
 * Fixed copyright notice comment.
 *
 * Revision 1.5  2000/04/05 19:01:12  robertj
 * Added function so can change desired transmit packet size.
 *
 * Revision 1.4  2000/03/22 01:29:43  robertj
 * Fixed default "frame" size for audio codecs, caused crash using G.711
 *
 * Revision 1.3  2000/03/21 03:06:50  robertj
 * Changes to make RTP TX of exact numbers of frames in some codecs.
 *
 * Revision 1.2  2000/02/16 03:24:27  robertj
 * Fixed bug in clamping maximum transmit packet size in G.711 capabilities.
 *
 * Revision 1.1  1999/12/23 23:02:36  robertj
 * File reorganision for separating RTP from H.323 and creation of LID for VPB support.
 *
 */

#ifdef __GNUC__
#pragma implementation "h323caps.h"
#endif

#include <ptlib.h>

#include "h323caps.h"
#include "h323ep.h"
#include "h323pdu.h"
#include "codec/opalplugin.h"
#include "mediafmt.h"

#include <algorithm>
#include <h323h224.h>

#define DEFINE_G711_CAPABILITY(cls, code, capName) \
class cls : public H323_G711Capability { \
  public: \
    cls() : H323_G711Capability(code) { } \
}; \
H323_REGISTER_CAPABILITY(cls, capName) \

#ifndef NO_H323_AUDIO_CODECS

DEFINE_G711_CAPABILITY(H323_G711ALaw64Capability, H323_G711Capability::ALaw, "G.711-ALaw-64k{sw}")
DEFINE_G711_CAPABILITY(H323_G711uLaw64Capability, H323_G711Capability::muLaw, "G.711-uLaw-64k{sw}")

#endif

#define new PNEW


#if PTRACING
ostream & operator<<(ostream & o , H323Capability::MainTypes t)
{
  const char * const names[] = {
    "Audio", "Video", "Data", "UserInput"
  };
  return o << names[t];
}

ostream & operator<<(ostream & o , H323Capability::CapabilityDirection d)
{
  const char * const names[] = {
    "Unknown", "Receive", "Transmit", "ReceiveAndTransmit", "NoDirection"
  };
  return o << names[d];
}
#endif


/////////////////////////////////////////////////////////////////////////////

H323Capability::H323Capability()
{
  assignedCapabilityNumber = 0; // Unassigned
  capabilityDirection = e_Unknown;
  rtpPayloadType = RTP_DataFrame::IllegalPayloadType;
}


PObject::Comparison H323Capability::Compare(const PObject & obj) const
{
  PAssert(PIsDescendant(&obj, H323Capability), PInvalidCast);
  const H323Capability & other = (const H323Capability &)obj;


  int mt = GetMainType();
  int omt = other.GetMainType();

  if (mt < omt)
    return LessThan;
  if (mt > omt)
    return GreaterThan;

  int st = GetSubType();
  int ost = other.GetSubType();

  if (st < ost)
    return LessThan;
  if (st > ost)
    return GreaterThan;

  return EqualTo;
}


void H323Capability::PrintOn(ostream & strm) const
{
  strm << GetFormatName();
  if (assignedCapabilityNumber != 0)
    strm << " <" << assignedCapabilityNumber << '>';
}


H323Capability * H323Capability::Create(const PString & name)
{
  H323Capability * cap = H323CapabilityFactory::CreateInstance(name);
  if (cap == NULL)
    return NULL;

  return (H323Capability *)cap->Clone();
}


unsigned H323Capability::GetDefaultSessionID() const
{
  return 0;
}


void H323Capability::SetTxFramesInPacket(unsigned /*frames*/)
{
}


unsigned H323Capability::GetTxFramesInPacket() const
{
  return 1;
}


unsigned H323Capability::GetRxFramesInPacket() const
{
  return 1;
}


BOOL H323Capability::IsMatch(const PASN_Choice & subTypePDU) const
{
  return subTypePDU.GetTag() == GetSubType();
}


BOOL H323Capability::OnReceivedPDU(const H245_Capability & cap)
{
  switch (cap.GetTag()) {
    case H245_Capability::e_receiveVideoCapability:
    case H245_Capability::e_receiveAudioCapability:
    case H245_Capability::e_receiveDataApplicationCapability:
    case H245_Capability::e_h233EncryptionReceiveCapability:
    case H245_Capability::e_receiveUserInputCapability:
      capabilityDirection = e_Receive;
      break;

    case H245_Capability::e_transmitVideoCapability:
    case H245_Capability::e_transmitAudioCapability:
    case H245_Capability::e_transmitDataApplicationCapability:
    case H245_Capability::e_h233EncryptionTransmitCapability:
    case H245_Capability::e_transmitUserInputCapability:
      capabilityDirection = e_Transmit;
      break;

    case H245_Capability::e_receiveAndTransmitVideoCapability:
    case H245_Capability::e_receiveAndTransmitAudioCapability:
    case H245_Capability::e_receiveAndTransmitDataApplicationCapability:
    case H245_Capability::e_receiveAndTransmitUserInputCapability:
      capabilityDirection = e_ReceiveAndTransmit;
      break;

    case H245_Capability::e_conferenceCapability:
	case H245_Capability::e_genericControlCapability:
    case H245_Capability::e_h235SecurityCapability:
    case H245_Capability::e_maxPendingReplacementFor:
      capabilityDirection = e_NoDirection;
  }

  return TRUE;
}


BOOL H323Capability::IsUsable(const H323Connection &) const
{
  return TRUE;
}


const OpalMediaFormat & H323Capability::GetMediaFormat() const
{
  return PRemoveConst(H323Capability, this)->GetWritableMediaFormat();
}


OpalMediaFormat & H323Capability::GetWritableMediaFormat()
{
  if (mediaFormat.IsEmpty()) {
    PString name = GetFormatName();
    name.Delete(name.FindLast('{'), 4);
    mediaFormat = OpalMediaFormat(name);
  }
  return mediaFormat;
}


/////////////////////////////////////////////////////////////////////////////

H323RealTimeCapability::H323RealTimeCapability()
{
    rtpqos = NULL;
}

H323RealTimeCapability::H323RealTimeCapability(const H323RealTimeCapability & rtc)
  : H323Capability(rtc)
{
#if P_HAS_QOS
  if (rtc.rtpqos != NULL) 
    rtpqos  = new RTP_QOS(*rtc.rtpqos);
  else
#endif
    rtpqos = NULL;
}

H323RealTimeCapability::~H323RealTimeCapability()
{
#if P_HAS_QOS
  delete rtpqos;
#endif
}

void H323RealTimeCapability::AttachQoS(RTP_QOS * _rtpqos)
{
#if P_HAS_QOS
  delete rtpqos;
#endif
    
  rtpqos = _rtpqos;
}

H323Channel * H323RealTimeCapability::CreateChannel(H323Connection & connection,
                                                    H323Channel::Directions dir,
                                                    unsigned sessionID,
                                 const H245_H2250LogicalChannelParameters * param) const
{
  return connection.CreateRealTimeLogicalChannel(*this, dir, sessionID, param, rtpqos);
}

BOOL H323RealTimeCapability::OnSendingPDU(H245_DataType & dataType) const
{
	const PString & algorithm = GetMediaFormat().GetOptionString("MediaEncryptionAlgorithm");
	if (algorithm != PString::Empty()) {
		dataType.SetTag(H245_DataType::e_h235Media);
		return ((H245_H235Media &)dataType).m_encryptionAuthenticationAndIntegrity.addEncryptionCapability(algorithm);
	}
	return FALSE;
}


/////////////////////////////////////////////////////////////////////////////

H323NonStandardCapabilityInfo::H323NonStandardCapabilityInfo(CompareFuncType _compareFunc,
                                                             const BYTE * dataPtr,
                                                             PINDEX dataSize)
  :
    t35CountryCode(0),
    t35Extension(0),
    manufacturerCode(0),
    nonStandardData(dataPtr, dataSize == 0 && dataPtr != NULL
                                 ? strlen((const char *)dataPtr) : dataSize),
    comparisonOffset(0),
    comparisonLength(0),
    compareFunc(_compareFunc)
{
}

H323NonStandardCapabilityInfo::H323NonStandardCapabilityInfo(const BYTE * dataPtr,
                                                             PINDEX dataSize,
                                                             PINDEX _offset,
                                                             PINDEX _len)
  : t35CountryCode(H323EndPoint::defaultT35CountryCode),
    t35Extension(H323EndPoint::defaultT35Extension),
    manufacturerCode(H323EndPoint::defaultManufacturerCode),
    nonStandardData(dataPtr, dataSize == 0 && dataPtr != NULL
                                 ? strlen((const char *)dataPtr) : dataSize),
    comparisonOffset(_offset),
    comparisonLength(_len),
    compareFunc(NULL)
{
}


H323NonStandardCapabilityInfo::H323NonStandardCapabilityInfo(const PString & _oid,
                                                             const BYTE * dataPtr,
                                                             PINDEX dataSize,
                                                             PINDEX _offset,
                                                             PINDEX _len)
  : oid(_oid),
    nonStandardData(dataPtr, dataSize == 0 && dataPtr != NULL
                                 ? strlen((const char *)dataPtr) : dataSize),
    comparisonOffset(_offset),
    comparisonLength(_len),
    compareFunc(NULL)
{
}


H323NonStandardCapabilityInfo::H323NonStandardCapabilityInfo(BYTE country,
                                                             BYTE extension,
                                                             WORD maufacturer,
                                                             const BYTE * dataPtr,
                                                             PINDEX dataSize,
                                                             PINDEX _offset,
                                                             PINDEX _len)
  : t35CountryCode(country),
    t35Extension(extension),
    manufacturerCode(maufacturer),
    nonStandardData(dataPtr, dataSize == 0 && dataPtr != NULL
                                 ? strlen((const char *)dataPtr) : dataSize),
    comparisonOffset(_offset),
    comparisonLength(_len),
    compareFunc(NULL)
{
}


H323NonStandardCapabilityInfo::~H323NonStandardCapabilityInfo()
{
}


BOOL H323NonStandardCapabilityInfo::OnSendingPDU(PBYTEArray & data) const
{
  data = nonStandardData;
  return data.GetSize() > 0;
}


BOOL H323NonStandardCapabilityInfo::OnReceivedPDU(const PBYTEArray & data)
{
  if (CompareData(data) != PObject::EqualTo)
    return FALSE;

  nonStandardData = data;
  return TRUE;
}


BOOL H323NonStandardCapabilityInfo::OnSendingNonStandardPDU(PASN_Choice & pdu,
                                                            unsigned nonStandardTag) const
{
  PBYTEArray data;
  if (!OnSendingPDU(data)) 
    return FALSE;

  pdu.SetTag(nonStandardTag);
  H245_NonStandardParameter & param = (H245_NonStandardParameter &)pdu.GetObject();

  if (!oid) {
    param.m_nonStandardIdentifier.SetTag(H245_NonStandardIdentifier::e_object);
    PASN_ObjectId & nonStandardIdentifier = param.m_nonStandardIdentifier;
    nonStandardIdentifier = oid;
  }
  else {
    param.m_nonStandardIdentifier.SetTag(H245_NonStandardIdentifier::e_h221NonStandard);
    H245_NonStandardIdentifier_h221NonStandard & h221 = param.m_nonStandardIdentifier;
    h221.m_t35CountryCode = (unsigned)t35CountryCode;
    h221.m_t35Extension = (unsigned)t35Extension;
    h221.m_manufacturerCode = (unsigned)manufacturerCode;
  }

  param.m_data = data;
  return data.GetSize() > 0;
}


BOOL H323NonStandardCapabilityInfo::OnReceivedNonStandardPDU(const PASN_Choice & pdu,
                                                             unsigned nonStandardTag)
{
  if (pdu.GetTag() != nonStandardTag)
    return FALSE;

  const H245_NonStandardParameter & param = (const H245_NonStandardParameter &)pdu.GetObject();

  if (CompareParam(param) != PObject::EqualTo)
    return FALSE;

  return OnReceivedPDU(param.m_data);
}


BOOL H323NonStandardCapabilityInfo::IsMatch(const H245_NonStandardParameter & param) const
{
  return CompareParam(param) == PObject::EqualTo && CompareData(param.m_data) == PObject::EqualTo;
}


PObject::Comparison H323NonStandardCapabilityInfo::CompareParam(const H245_NonStandardParameter & param) const
{
  if (compareFunc != NULL) {

    PluginCodec_H323NonStandardCodecData compareData;

    PString objectId;
    if (param.m_nonStandardIdentifier.GetTag() == H245_NonStandardIdentifier::e_object) {
      const PASN_ObjectId & nonStandardIdentifier = param.m_nonStandardIdentifier;
      objectId = nonStandardIdentifier.AsString();
      compareData.objectId = objectId;
    } else {
      const H245_NonStandardIdentifier_h221NonStandard & h221 = param.m_nonStandardIdentifier;
      compareData.objectId         = NULL;
      compareData.t35CountryCode   = (unsigned char)h221.m_t35CountryCode;
      compareData.t35Extension     = (unsigned char)h221.m_t35Extension;
      compareData.manufacturerCode = (unsigned short)h221.m_manufacturerCode;
    }
    const PBYTEArray & data = param.m_data;
    compareData.data       = (const unsigned char *)data;
    compareData.dataLength = data.GetSize();
    return (PObject::Comparison)(*compareFunc)(&compareData);
  }

  if (!oid) {
    if (param.m_nonStandardIdentifier.GetTag() != H245_NonStandardIdentifier::e_object)
      return PObject::LessThan;

    const PASN_ObjectId & nonStandardIdentifier = param.m_nonStandardIdentifier;
    PObject::Comparison cmp = oid.Compare(nonStandardIdentifier.AsString());
    if (cmp != PObject::EqualTo)
      return cmp;
  }
  else {
    if (param.m_nonStandardIdentifier.GetTag() != H245_NonStandardIdentifier::e_h221NonStandard)
      return PObject::LessThan;

    const H245_NonStandardIdentifier_h221NonStandard & h221 = param.m_nonStandardIdentifier;

    if (h221.m_t35CountryCode < (unsigned)t35CountryCode)
      return PObject::LessThan;
    if (h221.m_t35CountryCode > (unsigned)t35CountryCode)
      return PObject::GreaterThan;

    if (h221.m_t35Extension < (unsigned)t35Extension)
      return PObject::LessThan;
    if (h221.m_t35Extension > (unsigned)t35Extension)
      return PObject::GreaterThan;

    if (h221.m_manufacturerCode < (unsigned)manufacturerCode)
      return PObject::LessThan;
    if (h221.m_manufacturerCode > (unsigned)manufacturerCode)
      return PObject::GreaterThan;
  }

  return CompareData(param.m_data);
}


PObject::Comparison H323NonStandardCapabilityInfo::CompareInfo(const H323NonStandardCapabilityInfo & other) const
{
  if (compareFunc != NULL) {

    PluginCodec_H323NonStandardCodecData compareData;

    PString objectId;
    if (!other.oid.IsEmpty())
      compareData.objectId = other.oid;
    else {
      compareData.objectId         = NULL;
      compareData.t35CountryCode   = other.t35CountryCode;
      compareData.t35Extension     = other.t35Extension;
      compareData.manufacturerCode = other.manufacturerCode;
    }
    compareData.data       = (const unsigned char *)other.nonStandardData;
    compareData.dataLength = other.nonStandardData.GetSize();

    return (*compareFunc)(&compareData);
  }

  if (!oid) {
    if (other.oid.IsEmpty())
      return PObject::LessThan;

    PObject::Comparison cmp = oid.Compare(other.oid);
    if (cmp != PObject::EqualTo)
      return cmp;
  }
  else {
    if (other.t35CountryCode < t35CountryCode)
      return PObject::LessThan;
    if (other.t35CountryCode > t35CountryCode)
      return PObject::GreaterThan;

    if (other.t35Extension < t35Extension)
      return PObject::LessThan;
    if (other.t35Extension > t35Extension)
      return PObject::GreaterThan;

    if (other.manufacturerCode < manufacturerCode)
      return PObject::LessThan;
    if (other.manufacturerCode > manufacturerCode)
      return PObject::GreaterThan;
  }

  return CompareData(other.nonStandardData);
}


PObject::Comparison H323NonStandardCapabilityInfo::CompareData(const PBYTEArray & data) const
{
  if (comparisonOffset >= nonStandardData.GetSize())
    return PObject::LessThan;
  if (comparisonOffset >= data.GetSize())
    return PObject::GreaterThan;

  PINDEX len = comparisonLength;
  if (comparisonOffset+len > nonStandardData.GetSize())
    len = nonStandardData.GetSize() - comparisonOffset;

  if (comparisonOffset+len > data.GetSize())
    return PObject::GreaterThan;

  int cmp = memcmp((const BYTE *)nonStandardData + comparisonOffset,
                   (const BYTE *)data + comparisonOffset,
                   len);
  if (cmp < 0)
    return PObject::LessThan;
  if (cmp > 0)
    return PObject::GreaterThan;
  return PObject::EqualTo;
}


/////////////////////////////////////////////////////////////////////////////

H323GenericCapabilityInfo::H323GenericCapabilityInfo(const PString & standardId, PINDEX bitRate)
	: maxBitRate(bitRate)
{
  identifier = new H245_CapabilityIdentifier(H245_CapabilityIdentifier::e_standard);
  PASN_ObjectId & object_id = *identifier;
    object_id = standardId;
}


H323GenericCapabilityInfo::H323GenericCapabilityInfo(const H323GenericCapabilityInfo & obj)
  : maxBitRate(obj.maxBitRate)
{
  identifier = new H245_CapabilityIdentifier(*obj.identifier);
}


H323GenericCapabilityInfo::~H323GenericCapabilityInfo()
{
  delete identifier;
}


BOOL H323GenericCapabilityInfo::OnSendingGenericPDU(H245_GenericCapability & pdu,
                                                    const OpalMediaFormat & mediaFormat,
                                                    H323Capability::CommandType type) const
{
  pdu.m_capabilityIdentifier = *identifier;

#ifndef NO_H323_VIDEO
  //@@BSS unsigned bitRate = maxBitRate != 0 ? maxBitRate : ((mediaFormat.GetOptionInteger(OpalVideoFormat::MaxBitRateOption)+99)/100);
  unsigned bitRate = ((mediaFormat.GetOptionInteger(OpalVideoFormat::MaxBitRateOption)+99)/100); //@@BSS
#else
  unsigned bitRate = maxBitRate;
#endif
  if (bitRate != 0) {
    pdu.IncludeOptionalField(H245_GenericCapability::e_maxBitRate);
    pdu.m_maxBitRate = bitRate;
  }

  for (PINDEX i = 0; i < mediaFormat.GetOptionCount(); i++) {
    const OpalMediaOption & option = mediaFormat.GetOption(i);
    OpalMediaOption::H245GenericInfo genericInfo = option.GetH245Generic();
    if (genericInfo.mode == OpalMediaOption::H245GenericInfo::None)
      continue;

    switch (type) {
      case H323Capability::e_TCS :
        if (genericInfo.excludeTCS)
          continue;
        break;
      case H323Capability::e_OLC :
        if (genericInfo.excludeOLC)
          continue;
        break;
      case H323Capability::e_ReqMode :
        if (genericInfo.excludeReqMode)
          continue;
        break;
    }
    
    H245_GenericParameter * param = new H245_GenericParameter;

    param->m_parameterIdentifier.SetTag(H245_ParameterIdentifier::e_standard);
    (PASN_Integer &)param->m_parameterIdentifier = genericInfo.ordinal;

    if (PIsDescendant(&option, OpalMediaOptionBoolean)) {
      if (!((const OpalMediaOptionBoolean &)option).GetValue()) {
        delete param;
        continue; // Do not include a logical at all if it is false
      }
      param->m_parameterValue.SetTag(H245_ParameterValue::e_logical);
    }
    else if (PIsDescendant(&option, OpalMediaOptionUnsigned)) {
      unsigned tag;
      switch (genericInfo.integerType) {
        default :
        case OpalMediaOption::H245GenericInfo::UnsignedInt :
          tag = option.GetMerge() == OpalMediaOption::MinMerge ? H245_ParameterValue::e_unsignedMin : H245_ParameterValue::e_unsignedMax;
          break;

        case OpalMediaOption::H245GenericInfo::Unsigned32 :
          tag = option.GetMerge() == OpalMediaOption::MinMerge ? H245_ParameterValue::e_unsigned32Min : H245_ParameterValue::e_unsigned32Max;
          break;

        case OpalMediaOption::H245GenericInfo::BooleanArray :
          tag = H245_ParameterValue::e_booleanArray;
          break;
      }

      param->m_parameterValue.SetTag(tag);
      (PASN_Integer &)param->m_parameterValue = ((const OpalMediaOptionUnsigned &)option).GetValue();
    }
    else {
      param->m_parameterValue.SetTag(H245_ParameterValue::e_octetString);
      PASN_OctetString & octetString = param->m_parameterValue;
      if (PIsDescendant(&option, OpalMediaOptionOctets))
        octetString = ((const OpalMediaOptionOctets &)option).GetValue();
      else
        octetString = option.AsString();
    }

    if (genericInfo.mode == OpalMediaOption::H245GenericInfo::Collapsing) {
      pdu.IncludeOptionalField(H245_GenericCapability::e_collapsing);
      pdu.m_collapsing.Append(param);
    }
    else {
      pdu.IncludeOptionalField(H245_GenericCapability::e_nonCollapsing);
      pdu.m_nonCollapsing.Append(param);
    }
  }

	if (pdu.HasOptionalField(H245_GenericCapability::e_collapsing)) {
		H245_ArrayOf_GenericParameter & m_collapsing = pdu.m_collapsing;
		for (int i=0; i<m_collapsing.GetSize(); i++) {
			int id = (PASN_Integer &)m_collapsing[i].m_parameterIdentifier;
			int k = id==41 ? 0 : id==42 ? 1 : -1;
			if (k!=-1) {
				H245_GenericParameter temp=m_collapsing[k];
				m_collapsing[k]=m_collapsing[i];
				m_collapsing[i]=temp;
			}
		}
	}

  return TRUE;
}

BOOL H323GenericCapabilityInfo::OnReceivedGenericPDU(OpalMediaFormat & mediaFormat,
                                                     const H245_GenericCapability & pdu,
                                                     H323Capability::CommandType type)
{
  if (pdu.m_capabilityIdentifier != *identifier)
    return FALSE;

  if (pdu.HasOptionalField(H245_GenericCapability::e_maxBitRate)) {
    maxBitRate = pdu.m_maxBitRate;
#ifndef NO_H323_VIDEO
    mediaFormat.SetOptionInteger(OpalVideoFormat::MaxBitRateOption, maxBitRate*100);
#else
    mediaFormat.SetOptionInteger(maxBitRate, maxBitRate*100);
#endif
  }

	if (pdu.m_capabilityIdentifier.GetTag() == H245_CapabilityIdentifier::e_standard) {
		const PASN_ObjectId & object_id = pdu.m_capabilityIdentifier;
		if (object_id.AsString() == OpalPluginCodec_Identifer_H264_Generic)
			return TRUE;
	}

  for (PINDEX i = 0; i < mediaFormat.GetOptionCount(); i++) {
    const OpalMediaOption & option = mediaFormat.GetOption(i);
    OpalMediaOption::H245GenericInfo genericInfo = option.GetH245Generic();
    if (genericInfo.mode == OpalMediaOption::H245GenericInfo::None)
      continue;
    switch (type) {
      case H323Capability::e_TCS :
        if (genericInfo.excludeTCS)
          continue;
        break;
      case H323Capability::e_OLC :
        if (genericInfo.excludeOLC)
          continue;
        break;
      case H323Capability::e_ReqMode :
        if (genericInfo.excludeReqMode)
          continue;
        break;
    }

    const H245_ArrayOf_GenericParameter * params;
    if (genericInfo.mode == OpalMediaOption::H245GenericInfo::Collapsing) {
      if (!pdu.HasOptionalField(H245_GenericCapability::e_collapsing))
        continue;
      params = &pdu.m_collapsing;
    }
    else {
      if (!pdu.HasOptionalField(H245_GenericCapability::e_nonCollapsing))
        continue;
      params = &pdu.m_nonCollapsing;
    }

    if (PIsDescendant(&option, OpalMediaOptionBoolean))
      ((OpalMediaOptionBoolean &)option).SetValue(false);

    for (PINDEX j = 0; j < params->GetSize(); j++) {
      const H245_GenericParameter & param = (*params)[j];
      if (param.m_parameterIdentifier.GetTag() == H245_ParameterIdentifier::e_standard &&
                         (const PASN_Integer &)param.m_parameterIdentifier == genericInfo.ordinal) {
        if (PIsDescendant(&option, OpalMediaOptionBoolean)) {
          if (param.m_parameterValue.GetTag() == H245_ParameterValue::e_logical) {
            ((OpalMediaOptionBoolean &)option).SetValue(true);
            break;
          }
        }
        else if (PIsDescendant(&option, OpalMediaOptionUnsigned)) {
          unsigned tag;
          switch (genericInfo.integerType) {
            default :
            case OpalMediaOption::H245GenericInfo::UnsignedInt :
              tag = option.GetMerge() == OpalMediaOption::MinMerge ? H245_ParameterValue::e_unsignedMin : H245_ParameterValue::e_unsignedMax;
              break;
 
            case OpalMediaOption::H245GenericInfo::Unsigned32 :
              tag = option.GetMerge() == OpalMediaOption::MinMerge ? H245_ParameterValue::e_unsigned32Min : H245_ParameterValue::e_unsigned32Max;
              break;

            case OpalMediaOption::H245GenericInfo::BooleanArray :
              tag = H245_ParameterValue::e_booleanArray;
              break;
          }

          if (param.m_parameterValue.GetTag() == tag) {
            ((OpalMediaOptionUnsigned &)option).SetValue((const PASN_Integer &)param.m_parameterValue);
            break;
          }
        }
        else {
          if (param.m_parameterValue.GetTag() == H245_ParameterValue::e_octetString) {
            const PASN_OctetString & octetString = param.m_parameterValue;
            if (PIsDescendant(&option, OpalMediaOptionOctets))
              ((OpalMediaOptionOctets &)option).SetValue(octetString);
            else
              ((OpalMediaOption &)option).FromString(octetString.AsString());
            break;
    }
    }

        PTRACE(2, "Invalid generic parameter type (" << param.m_parameterValue.GetTagName()
               << ") for option \"" << option.GetName() << "\" (" << option.GetClass() << ')');
      }
    }
    }

    return TRUE;
}

BOOL H323GenericCapabilityInfo::IsMatch(const H245_GenericCapability & param) const
{
  return param.m_capabilityIdentifier == *identifier;
}

PObject::Comparison H323GenericCapabilityInfo::CompareInfo(const H323GenericCapabilityInfo & obj) const
{
  return identifier->Compare(*obj.identifier);
}


/////////////////////////////////////////////////////////////////////////////

#ifndef NO_H323_AUDIO_CODECS
int H323AudioCapability::DSCPvalue = PQoS::guaranteedDSCP;
H323AudioCapability::H323AudioCapability(unsigned rx, unsigned tx)
{
  rxFramesInPacket = rx;
  txFramesInPacket = tx;

#if P_HAS_QOS
// Set to G.729 Settings Avg 56kb/s Peek 110 kb/s
	rtpqos = new RTP_QOS;
	rtpqos->dataQoS.SetWinServiceType(SERVICETYPE_GUARANTEED);
	rtpqos->dataQoS.SetAvgBytesPerSec(7000);
	rtpqos->dataQoS.SetMaxFrameBytes(680);
	rtpqos->dataQoS.SetPeakBytesPerSec(13750);
	rtpqos->dataQoS.SetDSCP(DSCPvalue);

	rtpqos->ctrlQoS.SetWinServiceType(SERVICETYPE_CONTROLLEDLOAD);
	rtpqos->ctrlQoS.SetDSCP(PQoS::controlledLoadDSCP); 
#endif
}


H323Capability::MainTypes H323AudioCapability::GetMainType() const
{
  return e_Audio;
}


unsigned H323AudioCapability::GetDefaultSessionID() const
{
  return RTP_Session::DefaultAudioSessionID;
}


void H323AudioCapability::SetTxFramesInPacket(unsigned frames)
{
  PAssert(frames > 0, PInvalidParameter);
  if (frames > 256)
    txFramesInPacket = 256;
  else
    txFramesInPacket = frames;
}


unsigned H323AudioCapability::GetTxFramesInPacket() const
{
  return txFramesInPacket;
}


unsigned H323AudioCapability::GetRxFramesInPacket() const
{
  return rxFramesInPacket;
}

void H323AudioCapability::SetDSCPvalue(int newValue) 
{ 
	if (newValue < 64)
		DSCPvalue = newValue;
}

int H323AudioCapability::GetDSCPvalue() 
{ 
	return DSCPvalue; 
} 

BOOL H323AudioCapability::OnSendingPDU(H245_Capability & cap) const
{
  switch (capabilityDirection) {
    case e_Transmit:
      cap.SetTag(H245_Capability::e_transmitAudioCapability);
      break;
    case e_ReceiveAndTransmit:
      cap.SetTag(H245_Capability::e_receiveAndTransmitAudioCapability);
      break;
    case e_Receive :
    default:
      cap.SetTag(H245_Capability::e_receiveAudioCapability);
  }
  return OnSendingPDU((H245_AudioCapability &)cap, rxFramesInPacket, e_TCS);
}


BOOL H323AudioCapability::OnSendingPDU(H245_DataType & dataType) const
{
	(void)H323RealTimeCapability::OnSendingPDU(dataType);
	H245_AudioCapability *pac=0;
	if (dataType.GetTag()==H245_DataType::e_h235Media) {
		H245_H235Media_mediaType & mediaType = ((H245_H235Media &)dataType).m_mediaType;
		mediaType.SetTag(H245_H235Media_mediaType::e_audioData);
		pac = &((H245_AudioCapability &)mediaType);
	} else {
		dataType.SetTag(H245_DataType::e_audioData);
		pac = &((H245_AudioCapability &)dataType);
	}
	return pac && OnSendingPDU(*pac, txFramesInPacket, e_OLC);
}


BOOL H323AudioCapability::OnSendingPDU(H245_ModeElement & mode) const
{
  mode.m_type.SetTag(H245_ModeElementType::e_audioMode);
  return OnSendingPDU((H245_AudioMode &)mode.m_type);
}


BOOL H323AudioCapability::OnSendingPDU(H245_AudioCapability & pdu,
                                       unsigned packetSize) const
{
  pdu.SetTag(GetSubType());

  // Set the maximum number of frames
  PASN_Integer & value = pdu;
  value = packetSize;
	if (pdu.GetTag()==H245_AudioCapability::e_g722_64k ||
		pdu.GetTag()==H245_AudioCapability::e_g722_56k ||
		pdu.GetTag()==H245_AudioCapability::e_g722_48k ||
		pdu.GetTag()==H245_AudioCapability::e_g711Alaw64k ||
		pdu.GetTag()==H245_AudioCapability::e_g711Ulaw64k)
	{
		value=20;
	}
  return TRUE;
}


BOOL H323AudioCapability::OnSendingPDU(H245_AudioCapability & pdu,
                                       unsigned packetSize,
                                       CommandType) const
{
  return OnSendingPDU(pdu, packetSize);
}


BOOL H323AudioCapability::OnSendingPDU(H245_AudioMode & pdu) const
{
  static const H245_AudioMode::Choices AudioTable[] = {
    H245_AudioMode::e_nonStandard,
    H245_AudioMode::e_g711Alaw64k,
    H245_AudioMode::e_g711Alaw56k,
    H245_AudioMode::e_g711Ulaw64k,
    H245_AudioMode::e_g711Ulaw56k,
    H245_AudioMode::e_g722_64k,
    H245_AudioMode::e_g722_56k,
    H245_AudioMode::e_g722_48k,
    H245_AudioMode::e_g7231,
    H245_AudioMode::e_g728,
    H245_AudioMode::e_g729,
    H245_AudioMode::e_g729AnnexA,
    H245_AudioMode::e_is11172AudioMode,
    H245_AudioMode::e_is13818AudioMode,
    H245_AudioMode::e_g729wAnnexB,
    H245_AudioMode::e_g729AnnexAwAnnexB,
    H245_AudioMode::e_g7231AnnexCMode,
    H245_AudioMode::e_gsmFullRate,
    H245_AudioMode::e_gsmHalfRate,
    H245_AudioMode::e_gsmEnhancedFullRate,
    H245_AudioMode::e_genericAudioMode,
    H245_AudioMode::e_g729Extensions
  };

  unsigned subType = GetSubType();
  if (subType >= PARRAYSIZE(AudioTable))
    return FALSE;

  pdu.SetTag(AudioTable[subType]);
  return TRUE;
}


BOOL H323AudioCapability::OnReceivedPDU(const H245_Capability & cap)
{
  H323Capability::OnReceivedPDU(cap);

  if (cap.GetTag() != H245_Capability::e_receiveAudioCapability &&
      cap.GetTag() != H245_Capability::e_receiveAndTransmitAudioCapability)
    return FALSE;

  unsigned packetSize = txFramesInPacket;
  if (!OnReceivedPDU((const H245_AudioCapability &)cap, packetSize, e_TCS))
    return FALSE;

  // Clamp our transmit size to maximum allowed
  if (txFramesInPacket > packetSize) {
    PTRACE(3, "H323\tCapability tx frames reduced from "
           << txFramesInPacket << " to " << packetSize);
    txFramesInPacket = packetSize;
  }
  else {
    PTRACE(3, "H323\tCapability tx frames left at "
           << txFramesInPacket << " as remote allows " << packetSize);
  }

  return TRUE;
}


BOOL H323AudioCapability::OnReceivedPDU(const H245_DataType & dataType, BOOL receiver)
{
  const H245_AudioCapability *pac=0;
  if (dataType.GetTag() == H245_DataType::e_audioData)
	pac = &((const H245_AudioCapability &)dataType);
  else if (dataType.GetTag() == H245_DataType::e_h235Media) {
	const H245_H235Media_mediaType & mediaType = ((const H245_H235Media &)dataType).m_mediaType;
	if (mediaType.GetTag() == H245_H235Media_mediaType::e_audioData)
		pac = &((const H245_AudioCapability &)mediaType);
  }
  if (!pac)
    return FALSE;

  unsigned & xFramesInPacket = receiver ? rxFramesInPacket : txFramesInPacket;
  unsigned packetSize = xFramesInPacket;
  if (!OnReceivedPDU(*pac, packetSize, e_OLC))
    return FALSE;

  // Clamp our transmit size to maximum allowed
  if (xFramesInPacket > packetSize) {
    PTRACE(3, "H323\tCapability " << (receiver ? 'r' : 't') << "x frames reduced from "
           << xFramesInPacket << " to " << packetSize);
    xFramesInPacket = packetSize;
  }
  else {
    PTRACE(3, "H323\tCapability " << (receiver ? 'r' : 't') << "x frames left at "
           << xFramesInPacket << " as remote allows " << packetSize);
  }

  return TRUE;
}


BOOL H323AudioCapability::OnReceivedPDU(const H245_AudioCapability & pdu,
                                        unsigned & packetSize)
{
  if (pdu.GetTag() != GetSubType())
    return FALSE;

  const PASN_Integer & value = pdu;

  // Get the maximum number of frames
  packetSize = value;
  return TRUE;
}


BOOL H323AudioCapability::OnReceivedPDU(const H245_AudioCapability & pdu,
                                        unsigned & packetSize,
                                        CommandType)
{
  return OnReceivedPDU(pdu, packetSize);
}


/////////////////////////////////////////////////////////////////////////////

H323GenericAudioCapability::H323GenericAudioCapability(
      unsigned max,
      unsigned desired,
      const PString &standardId,
      PINDEX maxBitRate)
  : H323AudioCapability(max, desired),
    H323GenericCapabilityInfo(standardId, maxBitRate)
{
}

PObject::Comparison H323GenericAudioCapability::Compare(const PObject & obj) const
{
  if (!PIsDescendant(&obj, H323GenericAudioCapability))
    return LessThan;

  return CompareInfo((const H323GenericAudioCapability &)obj);
}


unsigned H323GenericAudioCapability::GetSubType() const
{
  return H245_AudioCapability::e_genericAudioCapability;
}


BOOL H323GenericAudioCapability::OnSendingPDU(H245_AudioCapability & pdu, unsigned, CommandType type) const
{
  pdu.SetTag(H245_AudioCapability::e_genericAudioCapability);
  return OnSendingGenericPDU(pdu, GetMediaFormat(), type);
}

BOOL H323GenericAudioCapability::OnSendingPDU(H245_AudioMode & pdu) const
{
  pdu.SetTag(H245_VideoMode::e_genericVideoMode);
  return OnSendingGenericPDU(pdu, GetMediaFormat(), e_ReqMode);
}

BOOL H323GenericAudioCapability::OnReceivedPDU(const H245_AudioCapability & pdu, unsigned &, CommandType type)
{
  if( pdu.GetTag() != H245_AudioCapability::e_genericAudioCapability)
    return FALSE;
  return OnReceivedGenericPDU(GetWritableMediaFormat(), pdu, type);
}

BOOL H323GenericAudioCapability::IsMatch(const PASN_Choice & subTypePDU) const
{
  return H323Capability::IsMatch(subTypePDU) &&
         H323GenericCapabilityInfo::IsMatch((const H245_GenericCapability &)subTypePDU.GetObject());
}



/////////////////////////////////////////////////////////////////////////////

H323NonStandardAudioCapability::H323NonStandardAudioCapability(
      unsigned max,
      unsigned desired,
      H323NonStandardCapabilityInfo::CompareFuncType compareFunc,
      const BYTE * fixedData,
      PINDEX dataSize
    )
  : H323AudioCapability(max, desired),
    H323NonStandardCapabilityInfo(compareFunc, fixedData, dataSize)
{
}

H323NonStandardAudioCapability::H323NonStandardAudioCapability(unsigned max,
                                                               unsigned desired,
                                                               const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323AudioCapability(max, desired),
    H323NonStandardCapabilityInfo(fixedData, dataSize, offset, length)
{
}

H323NonStandardAudioCapability::H323NonStandardAudioCapability(
      unsigned max,
      unsigned desired,
      H323EndPoint &,
      H323NonStandardCapabilityInfo::CompareFuncType compareFunc,
      const BYTE * fixedData,
      PINDEX dataSize)
  : H323AudioCapability(max, desired),
    H323NonStandardCapabilityInfo(compareFunc, fixedData, dataSize)
{
}

H323NonStandardAudioCapability::H323NonStandardAudioCapability(unsigned max,
                                                               unsigned desired,
                                                               H323EndPoint &,
                                                               const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323AudioCapability(max, desired),
    H323NonStandardCapabilityInfo(fixedData, dataSize, offset, length)
{
}

H323NonStandardAudioCapability::H323NonStandardAudioCapability(unsigned max,
                                                               unsigned desired,
                                                               const PString & oid,
                                                               const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323AudioCapability(max, desired),
    H323NonStandardCapabilityInfo(oid, fixedData, dataSize, offset, length)
{
}


H323NonStandardAudioCapability::H323NonStandardAudioCapability(unsigned max,
                                                               unsigned desired,
                                                               BYTE country,
                                                               BYTE extension,
                                                               WORD maufacturer,
                                                               const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323AudioCapability(max, desired),
    H323NonStandardCapabilityInfo(country, extension, maufacturer, fixedData, dataSize, offset, length)
{
}


PObject::Comparison H323NonStandardAudioCapability::Compare(const PObject & obj) const
{
  if (!PIsDescendant(&obj, H323NonStandardAudioCapability))
    return LessThan;

  return CompareInfo((const H323NonStandardAudioCapability &)obj);
}


unsigned H323NonStandardAudioCapability::GetSubType() const
{
  return H245_AudioCapability::e_nonStandard;
}


BOOL H323NonStandardAudioCapability::OnSendingPDU(H245_AudioCapability & pdu,
                                                  unsigned) const
{
  return OnSendingNonStandardPDU(pdu, H245_AudioCapability::e_nonStandard);
}


BOOL H323NonStandardAudioCapability::OnSendingPDU(H245_AudioMode & pdu) const
{
  return OnSendingNonStandardPDU(pdu, H245_AudioMode::e_nonStandard);
}


BOOL H323NonStandardAudioCapability::OnReceivedPDU(const H245_AudioCapability & pdu,
                                                   unsigned &)
{
  return OnReceivedNonStandardPDU(pdu, H245_AudioCapability::e_nonStandard);
}


BOOL H323NonStandardAudioCapability::IsMatch(const PASN_Choice & subTypePDU) const
{
  return H323Capability::IsMatch(subTypePDU) &&
         H323NonStandardCapabilityInfo::IsMatch((const H245_NonStandardParameter &)subTypePDU.GetObject());
}

#endif // NO_H323_AUDIO_CODECS


/////////////////////////////////////////////////////////////////////////////

#ifndef NO_H323_VIDEO
int H323VideoCapability::DSCPvalue = PQoS::controlledLoadDSCP;
H323VideoCapability::H323VideoCapability()
{
#if P_HAS_QOS
// Set to H263CIF Settings
	rtpqos = new RTP_QOS;
	rtpqos->dataQoS.SetWinServiceType(SERVICETYPE_CONTROLLEDLOAD);
	rtpqos->dataQoS.SetAvgBytesPerSec(16000);
	rtpqos->dataQoS.SetMaxFrameBytes(8192);
	rtpqos->dataQoS.SetDSCP(DSCPvalue);

	rtpqos->ctrlQoS.SetWinServiceType(SERVICETYPE_CONTROLLEDLOAD);
	rtpqos->ctrlQoS.SetDSCP(PQoS::controlledLoadDSCP); 
#endif
}

H323Capability::MainTypes H323VideoCapability::GetMainType() const
{
  return e_Video;
}


BOOL H323VideoCapability::OnSendingPDU(H245_Capability & cap) const
{
  switch (capabilityDirection) {
    case e_Transmit:
      cap.SetTag(H245_Capability::e_transmitVideoCapability);
      break;
    case e_ReceiveAndTransmit:
      cap.SetTag(H245_Capability::e_receiveAndTransmitVideoCapability);
      break;
    case e_Receive :
    default:
      cap.SetTag(H245_Capability::e_receiveVideoCapability);
  }
  return OnSendingPDU((H245_VideoCapability &)cap, e_TCS);
}


BOOL H323VideoCapability::OnSendingPDU(H245_DataType & dataType) const
{
	(void)H323RealTimeCapability::OnSendingPDU(dataType);
	H245_VideoCapability *pvc=0;
	if (dataType.GetTag()==H245_DataType::e_h235Media) {
		H245_H235Media_mediaType & mediaType = ((H245_H235Media &)dataType).m_mediaType;
		mediaType.SetTag(H245_H235Media_mediaType::e_videoData);
		pvc = &((H245_VideoCapability &)mediaType);
	} else {
		dataType.SetTag(H245_DataType::e_videoData);
		pvc = &((H245_VideoCapability &)dataType);
	}
	return pvc && OnSendingPDU(*pvc, e_OLC);
}


BOOL H323VideoCapability::OnSendingPDU(H245_VideoCapability & pdu) const
{
  return OnSendingPDU(pdu, e_TCS);
}


BOOL H323VideoCapability::OnSendingPDU(H245_VideoCapability & pdu, CommandType) const
{
  return OnSendingPDU(pdu);
}


BOOL H323VideoCapability::OnSendingPDU(H245_ModeElement & mode) const
{
  mode.m_type.SetTag(H245_ModeElementType::e_videoMode);
  return OnSendingPDU((H245_VideoMode &)mode.m_type);
}


BOOL H323VideoCapability::OnReceivedPDU(const H245_Capability & cap)
{
  H323Capability::OnReceivedPDU(cap);

  if (cap.GetTag() != H245_Capability::e_receiveVideoCapability &&
      cap.GetTag() != H245_Capability::e_receiveAndTransmitVideoCapability)
    return FALSE;

  return OnReceivedPDU((const H245_VideoCapability &)cap, e_TCS);
}


BOOL H323VideoCapability::OnReceivedPDU(const H245_DataType & dataType, BOOL)
{
  const H245_VideoCapability *pvc=0;
  if (dataType.GetTag() == H245_DataType::e_videoData)
	pvc = &((const H245_VideoCapability &)dataType);
  else if (dataType.GetTag() == H245_DataType::e_h235Media) {
	const H245_H235Media_mediaType & mediaType = ((const H245_H235Media &)dataType).m_mediaType;
	if (mediaType.GetTag() == H245_H235Media_mediaType::e_videoData)
		pvc = &((const H245_VideoCapability &)mediaType);
  }
  return pvc && OnReceivedPDU(*pvc, e_OLC);
}


BOOL H323VideoCapability::OnReceivedPDU(const H245_VideoCapability & pdu)
{
  return pdu.GetTag() == H245_VideoCapability::e_genericVideoCapability;
}


BOOL H323VideoCapability::OnReceivedPDU(const H245_VideoCapability & pdu, CommandType)
{
	if (pdu.GetTag() == H245_VideoCapability::e_extendedVideoCapability) {
		const H245_ExtendedVideoCapability & ec = (H245_ExtendedVideoCapability) pdu;
		const H245_ArrayOf_VideoCapability & cape = ec.m_videoCapability;
		for (int i=0; i<cape.GetSize(); i++) {
			const H245_VideoCapability & gcap = cape[i];
			if (gcap.GetTag()==H245_VideoCapability::e_h263VideoCapability ||
				gcap.GetTag()==H245_VideoCapability::e_genericVideoCapability)
			{
				return OnReceivedPDU(gcap);
			}
		}
		return FALSE;
	}
  return OnReceivedPDU(pdu);
}


unsigned H323VideoCapability::GetDefaultSessionID() const
{
  return RTP_Session::DefaultVideoSessionID;
}

void H323VideoCapability::SetDSCPvalue(int newValue) 
{ 
	if (newValue < 64)
		DSCPvalue = newValue;
}

int H323VideoCapability::GetDSCPvalue() 
{ 
	return DSCPvalue; 
}

/////////////////////////////////////////////////////////////////////////////

H323NonStandardVideoCapability::H323NonStandardVideoCapability(const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323NonStandardCapabilityInfo(fixedData, dataSize, offset, length)
{
}

H323NonStandardVideoCapability::H323NonStandardVideoCapability(H323EndPoint &,
                                                               const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323NonStandardCapabilityInfo(fixedData, dataSize, offset, length)
{
}

H323NonStandardVideoCapability::H323NonStandardVideoCapability(const PString & oid,
                                                               const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323NonStandardCapabilityInfo(oid, fixedData, dataSize, offset, length)
{
}


H323NonStandardVideoCapability::H323NonStandardVideoCapability(BYTE country,
                                                               BYTE extension,
                                                               WORD maufacturer,
                                                               const BYTE * fixedData,
                                                               PINDEX dataSize,
                                                               PINDEX offset,
                                                               PINDEX length)
  : H323NonStandardCapabilityInfo(country, extension, maufacturer, fixedData, dataSize, offset, length)
{
}


PObject::Comparison H323NonStandardVideoCapability::Compare(const PObject & obj) const
{
  if (!PIsDescendant(&obj, H323NonStandardVideoCapability))
    return LessThan;

  return CompareInfo((const H323NonStandardVideoCapability &)obj);
}


unsigned H323NonStandardVideoCapability::GetSubType() const
{
  return H245_VideoCapability::e_nonStandard;
}


BOOL H323NonStandardVideoCapability::OnSendingPDU(H245_VideoCapability & pdu) const
{
  return OnSendingNonStandardPDU(pdu, H245_VideoCapability::e_nonStandard);
}


BOOL H323NonStandardVideoCapability::OnSendingPDU(H245_VideoMode & pdu) const
{
  return OnSendingNonStandardPDU(pdu, H245_VideoMode::e_nonStandard);
}


BOOL H323NonStandardVideoCapability::OnReceivedPDU(const H245_VideoCapability & pdu)
{
  return OnReceivedNonStandardPDU(pdu, H245_VideoCapability::e_nonStandard);
}


BOOL H323NonStandardVideoCapability::IsMatch(const PASN_Choice & subTypePDU) const
{
  return H323Capability::IsMatch(subTypePDU) &&
         H323NonStandardCapabilityInfo::IsMatch((const H245_NonStandardParameter &)subTypePDU.GetObject());
}

/////////////////////////////////////////////////////////////////////////////

H323GenericVideoCapability::H323GenericVideoCapability(
      const PString &capabilityId,
      PINDEX maxBitRate)
        : H323VideoCapability(),
          H323GenericCapabilityInfo(capabilityId, maxBitRate)
{
}

PObject::Comparison H323GenericVideoCapability::Compare(const PObject & obj) const
{
  if (!PIsDescendant(&obj, H323GenericVideoCapability))
    return LessThan;

  return CompareInfo((const H323GenericVideoCapability &)obj);
}


unsigned H323GenericVideoCapability::GetSubType() const
{
  return H245_VideoCapability::e_genericVideoCapability;
}


BOOL H323GenericVideoCapability::OnSendingPDU(H245_VideoCapability & pdu, CommandType type) const
{
  pdu.SetTag(H245_VideoCapability::e_genericVideoCapability);
  return OnSendingGenericPDU(pdu, GetMediaFormat(), type);
}

BOOL H323GenericVideoCapability::OnSendingPDU(H245_VideoMode & pdu) const
{
  pdu.SetTag(H245_VideoMode::e_genericVideoMode);
  return OnSendingGenericPDU(pdu, GetMediaFormat(), e_ReqMode);
}

BOOL H323GenericVideoCapability::OnReceivedPDU(const H245_VideoCapability & pdu, CommandType type)
{
  if (pdu.GetTag() == H245_VideoCapability::e_extendedVideoCapability)
	return TRUE;
  if (pdu.GetTag() != H245_VideoCapability::e_genericVideoCapability)
	return FALSE;
  return OnReceivedGenericPDU(GetWritableMediaFormat(), pdu, type);
}

BOOL H323GenericVideoCapability::IsMatch(const PASN_Choice & subTypePDU) const
{
  return H323Capability::IsMatch(subTypePDU) &&
         H323GenericCapabilityInfo::IsMatch((const H245_GenericCapability &)subTypePDU.GetObject());
}

/////////////////////////////////////////////////////////////////////////////

#ifdef H323_H239

H323ExtendedVideoCapability::H323ExtendedVideoCapability(
      const PString &capabilityId )
        : H323Capability(),
          H323GenericCapabilityInfo(capabilityId, 0), m_dscp(-1)
{
	SetCapabilityDirection(H323Capability::e_NoDirection);
}

BOOL H323ExtendedVideoCapability::OnSendingPDU(H245_Capability & cap) const
{
	  cap.SetTag(H245_Capability::e_genericControlCapability);
	  return OnSendingPDU((H245_GenericCapability &)cap, e_TCS);
}

BOOL H323ExtendedVideoCapability::OnReceivedPDU(const H245_Capability & cap)
{
  H323Capability::OnReceivedPDU(cap);

  if( cap.GetTag()!= H245_Capability::e_genericControlCapability)
    return FALSE;

  return OnReceivedPDU((const H245_GenericCapability &)cap, e_TCS);
} 

BOOL H323ExtendedVideoCapability::OnReceivedPDU(const H245_GenericCapability & pdu, CommandType type)
{
  OpalMediaFormat mediaFormat = GetMediaFormat();
  return OnReceivedGenericPDU(mediaFormat, pdu, type);
}

BOOL H323ExtendedVideoCapability::OnSendingPDU(H245_GenericCapability & pdu, CommandType type) const
{
  return OnSendingGenericPDU(pdu, GetMediaFormat(), type);
}

PObject::Comparison H323ExtendedVideoCapability::Compare(const PObject & obj) const
{
  if (!PIsDescendant(&obj, H323ExtendedVideoCapability))
    return LessThan;

  return CompareInfo((const H323ExtendedVideoCapability &)obj);
}

H323Capability::MainTypes H323ExtendedVideoCapability::GetMainType() const
{
	return H323Capability::e_GenericControl;
}

unsigned H323ExtendedVideoCapability::GetSubType() const
{
	return H245_Capability::e_genericControlCapability;
}

unsigned H323ExtendedVideoCapability::GetDefaultSessionID() const
{
	return OpalMediaFormat::DefaultExtVideoSessionID;
}

void H323ExtendedVideoCapability::PrintOn(ostream & strm) const
{
  if (table.GetSize() > 0) {
	  strm << "H239<" << table[0] << ">";
	  return;
  }

  strm << GetFormatName();
  if (assignedCapabilityNumber != 0)
    strm << " <" << assignedCapabilityNumber << '>';

	if (extCapabilities.GetSize() > 0) {
	  int indent = strm.precision() + 2;
      for (PINDEX i=0; i< extCapabilities.GetSize(); i++) {
         strm << '\n' << setw(indent+6) << extCapabilities[i];
	  }
	}
}

H323Channel * H323ExtendedVideoCapability::CreateChannel(H323Connection & connection,   
      H323Channel::Directions dir,unsigned sessionID,const H245_H2250LogicalChannelParameters * param
) const
{
   H323Capability *capability=0;
   if ((capability=extCapabilities.FindCapability(m_capabilityNumber))) {       
     if (m_dscp != -1) {                                                        
       H323RealTimeCapability* c = (H323RealTimeCapability*) capability;        
       c->rtpqos->dataQoS.SetDSCP( m_dscp );                                    
     }
     return capability->CreateChannel(connection,dir,sessionID,param);
   }
   else
	 return NULL;
}

BOOL H323ExtendedVideoCapability::OnSendingPDU(H245_DataType & pdu) const
{
	const PString & algorithm = GetMediaFormat().GetOptionString("MediaEncryptionAlgorithm");
	if (algorithm != PString::Empty()) {
		pdu.SetTag(H245_DataType::e_h235Media);
		return ((H245_H235Media &)pdu).m_encryptionAuthenticationAndIntegrity.addEncryptionCapability(algorithm);
	}
	return FALSE;
}

BOOL H323ExtendedVideoCapability::OnSendingPDU(H245_ModeElement & pdu) const
{
   if (table.GetSize() > 0)
	 return table[0].OnSendingPDU(pdu);
   else
	 return FALSE;
}

BOOL H323ExtendedVideoCapability::OnReceivedPDU(const H245_DataType & pdu, BOOL receiver)
{
   if (table.GetSize() > 0)
	 return table[0].OnReceivedPDU(pdu,receiver);
   else
	 return FALSE;
}

BOOL H323ExtendedVideoCapability::IsMatch(const PASN_Choice & subTypePDU) const
{
  return H323Capability::IsMatch(subTypePDU) &&
         H323GenericCapabilityInfo::IsMatch((const H245_GenericCapability &)subTypePDU.GetObject());
}

void H323ExtendedVideoCapability::AddAllCapabilities(
      H323Capabilities & basecapabilities, PINDEX descriptorNum,PINDEX simultaneous)
{
  H323ExtendedVideoFactory::KeyList_T extCaps = H323ExtendedVideoFactory::GetKeyList();
  if (extCaps.size() > 0) {
	  H323CodecExtendedVideoCapability * extCapability = new H323CodecExtendedVideoCapability();
    // Add all the extended Video Capabilities to the capability list
    H323ExtendedVideoFactory::KeyList_T::const_iterator r;
    for (r = extCaps.begin(); r != extCaps.end(); ++r) {
       PString capName(*r);
       H323Capability * capability = H323ExtendedVideoFactory::CreateInstance(capName);
	   extCapability->AddCapability(capability);
	   H323ExtendedVideoFactory::Unregister(*r);  // To avoid nasty segfault on shutdown.
	}
      basecapabilities.SetCapability(descriptorNum, simultaneous,extCapability);
 // control not yet available
 //     basecapabilities.SetCapability(descriptorNum, simultaneous, new H323ControlExtendedVideoCapability()); 
  } else {
	  PTRACE(3,"EXT\tNo Extended Capabilities found to load");
  }
}

H323Capability & H323ExtendedVideoCapability::operator[](PINDEX i) 
{
  return extCapabilities[i];
}

///////////////////////////////////////////////////////////////////////////////////////////////////


H323ControlExtendedVideoCapability::H323ControlExtendedVideoCapability()
  : H323ExtendedVideoCapability(OpalPluginCodec_Identifer_H239)
{ 
}

PString H323ControlExtendedVideoCapability::GetFormatName() const
{
      return "H.239 Control";
}

///////////////////////////////////////////////////////////////////////////////////////////////////

H323CodecExtendedVideoCapability::H323CodecExtendedVideoCapability()
   : H323ExtendedVideoCapability(OpalPluginCodec_Identifer_H239_Video)
{
	
   SetCapabilityDirection(H323Capability::e_Transmit);
}

H323CodecExtendedVideoCapability::~H323CodecExtendedVideoCapability()
{
}

void H323CodecExtendedVideoCapability::AddCapability(H323Capability * capability)
{
	extCapabilities.Add(capability);
}

PString H323CodecExtendedVideoCapability::GetFormatName() const
{
	if (extCapabilities.GetSize()==1)
		return extCapabilities[0].GetFormatName();
	return "H.239 Capabilities";
}

H323Capability::MainTypes H323CodecExtendedVideoCapability::GetMainType() const
{  
	return H323Capability::e_Video; 
}

unsigned H323CodecExtendedVideoCapability::GetSubType() const
{  
	return H245_VideoCapability::e_extendedVideoCapability;  
}

BOOL H323CodecExtendedVideoCapability::OnSendingPDU(H245_Capability & cap) const
{
   cap.SetTag(H245_Capability::e_receiveVideoCapability);
   return OnSendingPDU((H245_VideoCapability &)cap);
}

BOOL H323CodecExtendedVideoCapability::OnReceivedPDU(const H245_Capability & cap)
{
  H323Capability::OnReceivedPDU(cap);

  if (cap.GetTag() != H245_Capability::e_receiveVideoCapability &&
      cap.GetTag() != H245_Capability::e_receiveAndTransmitVideoCapability)
    return FALSE;

  return OnReceivedPDU((const H245_VideoCapability &)cap);
} 

BOOL H323CodecExtendedVideoCapability::OnSendingPDU(H245_VideoCapability & pdu) const 
{ 
	if (extCapabilities.GetSize() == 0)
		return FALSE;

	pdu.SetTag(H245_VideoCapability::e_extendedVideoCapability);
    H245_ExtendedVideoCapability & extend = (H245_ExtendedVideoCapability &)pdu;

	extend.IncludeOptionalField(H245_ExtendedVideoCapability::e_videoCapabilityExtension);
    H245_ArrayOf_GenericCapability & cape = extend.m_videoCapabilityExtension;

	H245_GenericCapability gcap;
	 gcap.m_capabilityIdentifier = *(new H245_CapabilityIdentifier(H245_CapabilityIdentifier::e_standard));
	 PASN_ObjectId &object_id = gcap.m_capabilityIdentifier;
     object_id = OpalPluginCodec_Identifer_H239_Video;

	 // Add role
      H245_GenericParameter * param = new H245_GenericParameter;
      param->m_parameterIdentifier.SetTag(H245_ParameterIdentifier::e_standard);
      (PASN_Integer &)param->m_parameterIdentifier = 1;
	  param->m_parameterValue.SetTag(H245_ParameterValue::e_booleanArray);
      (PASN_Integer &)param->m_parameterValue = 1;  // Presentation Role

      gcap.IncludeOptionalField(H245_GenericCapability::e_collapsing);
      gcap.m_collapsing.Append(param);
      cape.SetSize(1);
      cape[0] = gcap;


    H245_ArrayOf_VideoCapability & caps = extend.m_videoCapability;
    
	if (table.GetSize() > 0) {
	  caps.SetSize(table.GetSize());
      for (PINDEX i=0; i< table.GetSize(); i++) {
	     H245_VideoCapability vidcap;
        ((H323VideoCapability &)table[i]).OnSendingPDU(vidcap);
        caps[i] = vidcap;
	  }
	} else {
	  caps.SetSize(extCapabilities.GetSize());
      for (PINDEX i=0; i< extCapabilities.GetSize(); i++) {
	     H245_VideoCapability vidcap;
         ((H323VideoCapability &)extCapabilities[i]).OnSendingPDU(vidcap);
         caps[i] = vidcap;
      }
	}

  return TRUE;
}

BOOL H323CodecExtendedVideoCapability::OnReceivedPDU(const H245_VideoCapability & pdu )
{ 
   if (pdu.GetTag() != H245_VideoCapability::e_extendedVideoCapability)
		return FALSE;

   const H245_ExtendedVideoCapability & extend = (const H245_ExtendedVideoCapability &)pdu;

   if (!extend.HasOptionalField(H245_ExtendedVideoCapability::e_videoCapabilityExtension))
		return FALSE;

   // Role Information
   const H245_ArrayOf_GenericCapability & cape = extend.m_videoCapabilityExtension;
   if (cape.GetSize() == 0) {
	   PTRACE(2,"H239\tERROR: Missing Capability Extension!");
	   return FALSE;
   }

   for (PINDEX b =0; b < cape.GetSize(); b++) {
    const H245_GenericCapability & cap = cape[b];
	if (cap.m_capabilityIdentifier.GetTag() != H245_CapabilityIdentifier::e_standard) {
		PTRACE(3,"H239\tERROR: Wrong Capability type!");
		return FALSE;
	}
	
	const PASN_ObjectId & id = cap.m_capabilityIdentifier;
	if (id != OpalPluginCodec_Identifer_H239_Video) {
		PTRACE(3,"H239\tERROR: Wrong Capability Identifer " << id);
		return FALSE;
	}

	if (!cap.HasOptionalField(H245_GenericCapability::e_collapsing)) {
		PTRACE(3,"H239\tERROR: No nonCollapsing field");
		return FALSE;
	}

	for (PINDEX c =0; c < cap.m_collapsing.GetSize(); c++) {
		const H245_GenericParameter & param = cap.m_collapsing[c];
		const PASN_Integer & id = param.m_parameterIdentifier;
		if (id.GetValue() != 1) {
	        PTRACE(3,"H239\tERROR: Unknown Role Identifer");
			return FALSE;
		}
		const PASN_Integer & role = param.m_parameterValue;
		if (role.GetValue() != 1 && role.GetValue() != 3) {
	        PTRACE(3,"H239\tERROR: Unsupported Role mode " << param.m_parameterValue );
			return FALSE;
		}
	}
  }

  // Get a Common Video Capability list
  const H245_ArrayOf_VideoCapability & caps = extend.m_videoCapability;
  H323Capabilities allCapabilities;
  for (PINDEX c = 0; c < extCapabilities.GetSize(); c++)
    allCapabilities.Add(allCapabilities.Copy(extCapabilities[c]));

  // Decode out of the PDU, the list of known codecs.
	m_capabilityNumber=0;
    for (PINDEX i = 0; i < caps.GetSize(); i++) {
        H323Capability * capability = allCapabilities.FindCapability(H323Capability::e_Video, caps[i], NULL);
        if (capability != NULL) {
          if (((H323VideoCapability*)capability)->OnReceivedPDU(caps[i])) {
            m_capabilityNumber=capability->GetCapabilityNumber();
			break;
		  }
        }
    }
	return m_capabilityNumber!=0;
}

BOOL H323CodecExtendedVideoCapability::OnSendingPDU(H245_DataType & pdu) const
{
	(void)H323ExtendedVideoCapability::OnSendingPDU(pdu);
	H245_VideoCapability *pvc=0;
	if (pdu.GetTag()==H245_DataType::e_h235Media) {
		H245_H235Media_mediaType & mediaType = ((H245_H235Media &)pdu).m_mediaType;
		mediaType.SetTag(H245_H235Media_mediaType::e_videoData);
		pvc = &((H245_VideoCapability &)mediaType);
	} else {
		pdu.SetTag(H245_DataType::e_videoData);
		pvc = &((H245_VideoCapability &)pdu);
	}
	return pvc && OnSendingPDU(*pvc);
}

BOOL H323CodecExtendedVideoCapability::OnReceivedPDU(const H245_DataType & pdu, BOOL receiver) {
	const H245_VideoCapability *pvc=0;
	if (pdu.GetTag()==H245_DataType::e_videoData)
		pvc = &((const H245_VideoCapability &)pdu);
	else if (pdu.GetTag()==H245_DataType::e_h235Media) {
		const H245_H235Media_mediaType & mediaType = ((const H245_H235Media &)pdu).m_mediaType;
		if (mediaType.GetTag()==H245_H235Media_mediaType::e_videoData)
			pvc = &((const H245_VideoCapability &)mediaType);
	}
	return pvc && OnReceivedPDU(*pvc);
}

BOOL H323CodecExtendedVideoCapability::IsMatch(const PASN_Choice & subTypePDU) const
{
   return subTypePDU.GetTag() == GetSubType();
}

BOOL H323CodecExtendedVideoCapability::OnReceivedGenericPDU(const H245_GenericCapability &pdu)
{
	return TRUE;
}

#endif // H323_H239
#endif // NO_H323_VIDEO

/////////////////////////////////////////////////////////////////////////////

#ifdef H323_H230
H323_ConferenceControlCapability::H323_ConferenceControlCapability()
{
   chairControlCapability = FALSE;
   nonStandardExtension = FALSE;
}

H323_ConferenceControlCapability::H323_ConferenceControlCapability(BOOL chairControls, BOOL T124Extension)
{
   chairControlCapability = chairControls;
   nonStandardExtension = T124Extension;
}

PObject * H323_ConferenceControlCapability::Clone() const
{
  return new H323_ConferenceControlCapability(*this);
}

H323Capability::MainTypes H323_ConferenceControlCapability::GetMainType() const
{
  return e_ConferenceControl;
}

unsigned H323_ConferenceControlCapability::GetSubType()  const
{
    return 0;
}

PString H323_ConferenceControlCapability::GetFormatName() const
{
  return "H.230 Conference Controls";
}

H323Channel * H323_ConferenceControlCapability::CreateChannel(H323Connection &,
                                                      H323Channel::Directions,
                                                      unsigned,
                                                      const H245_H2250LogicalChannelParameters *) const
{
  PTRACE(1, "Codec\tCannot create ConferenceControlCapability channel");
  return NULL;
}

H323Codec * H323_ConferenceControlCapability::CreateCodec(H323Codec::Direction) const
{
  PTRACE(1, "Codec\tCannot create ConferenceControlCapability codec");
  return NULL;
}

static const char * ExtConferenceControlOID = "0.0.20.124.2";  // Tunnel T.124
BOOL H323_ConferenceControlCapability::OnSendingPDU(H245_Capability & pdu) const
{
   
  pdu.SetTag(H245_Capability::e_conferenceCapability);
  H245_ConferenceCapability & conf = pdu;
  // Supports Chair control
  conf.m_chairControlCapability = chairControlCapability;

  // Include Extended Custom Controls such as INVITE/EJECT etc.
  if (nonStandardExtension) {
	conf.IncludeOptionalField(H245_ConferenceCapability::e_nonStandardData);
	H245_ArrayOf_NonStandardParameter & nsParam = conf.m_nonStandardData;

	H245_NonStandardParameter param;
	H245_NonStandardIdentifier & id = param.m_nonStandardIdentifier;
	id.SetTag(H245_NonStandardIdentifier::e_object);
	PASN_ObjectId & oid = id;
	oid.SetValue(ExtConferenceControlOID);
	PASN_OctetString & data = param.m_data;
	data.SetValue("");

	nsParam.SetSize(1);
	nsParam[0] = param;
  }

  return TRUE;
}

BOOL H323_ConferenceControlCapability::OnSendingPDU(H245_DataType &) const
{
  PTRACE(1, "Codec\tCannot have ConferenceControlCapability in DataType");
  return FALSE;
}

BOOL H323_ConferenceControlCapability::OnSendingPDU(H245_ModeElement &) const
{
  PTRACE(1, "Codec\tCannot have ConferenceControlCapability in ModeElement");
  return FALSE;
}

BOOL H323_ConferenceControlCapability::OnReceivedPDU(const H245_Capability & pdu)
{

  H323Capability::OnReceivedPDU(pdu);

  if (pdu.GetTag() != H245_Capability::e_conferenceCapability)
    return FALSE;

  const H245_ConferenceCapability & conf = pdu;
  // Supports Chair control
  chairControlCapability = conf.m_chairControlCapability;

  // Include Extended Custom Control support.
  if (conf.HasOptionalField(H245_ConferenceCapability::e_nonStandardData)) {
	const H245_ArrayOf_NonStandardParameter & nsParam = conf.m_nonStandardData;

	for (PINDEX i=0; i < nsParam.GetSize(); i++) {
		const H245_NonStandardParameter & param = nsParam[i];
	    const H245_NonStandardIdentifier & id = param.m_nonStandardIdentifier;
		if (id.GetTag() == H245_NonStandardIdentifier::e_object) {
             const PASN_ObjectId & oid = id;
			 if (oid.AsString() == ExtConferenceControlOID)
				    nonStandardExtension = TRUE;
		}
	}
  }
  return TRUE;
}

BOOL H323_ConferenceControlCapability::OnReceivedPDU(const H245_DataType &, BOOL)
{
  PTRACE(1, "Codec\tCannot have ConferenceControlCapability in DataType");
  return FALSE;
}

#endif  // H323_H230

/////////////////////////////////////////////////////////////////////////////

H323DataCapability::H323DataCapability(unsigned rate)
  : maxBitRate(rate)
{
  m_mediaEncryptionAlgorithm.MakeEmpty();
}


H323Capability::MainTypes H323DataCapability::GetMainType() const
{
  return e_Data;
}


unsigned H323DataCapability::GetDefaultSessionID() const
{
  return 3;
}


H323Codec * H323DataCapability::CreateCodec(H323Codec::Direction) const
{
  return NULL;
}


BOOL H323DataCapability::OnSendingPDU(H245_Capability & cap) const
{
  switch (capabilityDirection) {
    case e_Transmit:
      cap.SetTag(H245_Capability::e_transmitDataApplicationCapability);
      break;
    case e_Receive :
      cap.SetTag(H245_Capability::e_receiveDataApplicationCapability);
      break;
    case e_ReceiveAndTransmit:
    default:
      cap.SetTag(H245_Capability::e_receiveAndTransmitDataApplicationCapability);
  }
  H245_DataApplicationCapability & app = cap;
  app.m_maxBitRate = maxBitRate;
  return OnSendingPDU(app, e_TCS);
}


BOOL H323DataCapability::OnSendingPDU(H245_DataType & dataType) const
{
	H245_DataApplicationCapability *pdc=0;
	PString algorithm;
	if (GetFormatName()=="H.224" && (algorithm = ((H323DataCapability *)this)->getMediaEncryptionAlgorithm()) != PString::Empty()) {
		dataType.SetTag(H245_DataType::e_h235Media);
		if (((H245_H235Media &)dataType).m_encryptionAuthenticationAndIntegrity.addEncryptionCapability(algorithm)) {
			H245_H235Media_mediaType & mediaType = ((H245_H235Media &)dataType).m_mediaType;
			mediaType.SetTag(H245_H235Media_mediaType::e_data);
			pdc = &((H245_DataApplicationCapability &)mediaType);
		}
	} else {
		dataType.SetTag(H245_DataType::e_data);
		pdc = &((H245_DataApplicationCapability &)dataType);
	}
	return pdc && OnSendingPDU(*pdc, e_OLC);
}


BOOL H323DataCapability::OnSendingPDU(H245_ModeElement & mode) const
{
  mode.m_type.SetTag(H245_ModeElementType::e_dataMode);
  H245_DataMode & type = mode.m_type;
  type.m_bitRate = maxBitRate;
  return OnSendingPDU(type);
}


BOOL H323DataCapability::OnSendingPDU(H245_DataApplicationCapability &) const
{
  return FALSE;
}


BOOL H323DataCapability::OnSendingPDU(H245_DataApplicationCapability & pdu, CommandType type) const
{
  if (type==e_OLC)
	pdu.m_maxBitRate = maxBitRate;
  return OnSendingPDU(pdu);
}


BOOL H323DataCapability::OnReceivedPDU(const H245_Capability & cap)
{
  H323Capability::OnReceivedPDU(cap);

  if (cap.GetTag() != H245_Capability::e_receiveDataApplicationCapability &&
      cap.GetTag() != H245_Capability::e_receiveAndTransmitDataApplicationCapability)
    return FALSE;

  const H245_DataApplicationCapability & app = cap;
  maxBitRate = app.m_maxBitRate;
  return OnReceivedPDU(app, e_TCS);
}


BOOL H323DataCapability::OnReceivedPDU(const H245_DataType & dataType, BOOL)
{
	const H245_DataApplicationCapability *pdc=0;
	if (dataType.GetTag() == H245_DataType::e_data)
		pdc = &((const H245_DataApplicationCapability &)dataType);
	else if (dataType.GetTag() == H245_DataType::e_h235Media) {
		const H245_H235Media_mediaType & mediaType = ((const H245_H235Media &)dataType).m_mediaType;
		if (mediaType.GetTag() == H245_H235Media_mediaType::e_data)
			pdc = &((const H245_DataApplicationCapability &)mediaType);
	}
	return pdc && OnReceivedPDU(*pdc, e_OLC);
}


BOOL H323DataCapability::OnReceivedPDU(const H245_DataApplicationCapability &)
{
  return FALSE;
}


BOOL H323DataCapability::OnReceivedPDU(const H245_DataApplicationCapability & pdu, CommandType type)
{
  if (type==e_OLC)
	maxBitRate = pdu.m_maxBitRate;
  return OnReceivedPDU(pdu);
}


/////////////////////////////////////////////////////////////////////////////

H323NonStandardDataCapability::H323NonStandardDataCapability(unsigned maxBitRate,
                                                             const BYTE * fixedData,
                                                             PINDEX dataSize,
                                                             PINDEX offset,
                                                             PINDEX length)
  : H323DataCapability(maxBitRate),
    H323NonStandardCapabilityInfo(fixedData, dataSize, offset, length)
{
}


H323NonStandardDataCapability::H323NonStandardDataCapability(unsigned maxBitRate,
                                                             const PString & oid,
                                                             const BYTE * fixedData,
                                                             PINDEX dataSize,
                                                             PINDEX offset,
                                                             PINDEX length)
  : H323DataCapability(maxBitRate),
    H323NonStandardCapabilityInfo(oid, fixedData, dataSize, offset, length)
{
}


H323NonStandardDataCapability::H323NonStandardDataCapability(unsigned maxBitRate,
                                                             BYTE country,
                                                             BYTE extension,
                                                             WORD maufacturer,
                                                             const BYTE * fixedData,
                                                             PINDEX dataSize,
                                                             PINDEX offset,
                                                             PINDEX length)
  : H323DataCapability(maxBitRate),
    H323NonStandardCapabilityInfo(country, extension, maufacturer, fixedData, dataSize, offset, length)
{
}


PObject::Comparison H323NonStandardDataCapability::Compare(const PObject & obj) const
{
  if (!PIsDescendant(&obj, H323NonStandardDataCapability))
    return LessThan;

  return CompareInfo((const H323NonStandardDataCapability &)obj);
}


unsigned H323NonStandardDataCapability::GetSubType() const
{
  return H245_DataApplicationCapability_application::e_nonStandard;
}


BOOL H323NonStandardDataCapability::OnSendingPDU(H245_DataApplicationCapability & pdu) const
{
  return OnSendingNonStandardPDU(pdu.m_application, H245_DataApplicationCapability_application::e_nonStandard);
}


BOOL H323NonStandardDataCapability::OnSendingPDU(H245_DataMode & pdu) const
{
  return OnSendingNonStandardPDU(pdu.m_application, H245_DataMode_application::e_nonStandard);
}


BOOL H323NonStandardDataCapability::OnReceivedPDU(const H245_DataApplicationCapability & pdu)
{
  return OnReceivedNonStandardPDU(pdu.m_application, H245_DataApplicationCapability_application::e_nonStandard);
}


BOOL H323NonStandardDataCapability::IsMatch(const PASN_Choice & subTypePDU) const
{
  return H323Capability::IsMatch(subTypePDU) &&
         H323NonStandardCapabilityInfo::IsMatch((const H245_NonStandardParameter &)subTypePDU.GetObject());
}


/////////////////////////////////////////////////////////////////////////////

#ifndef NO_H323_AUDIO_CODECS

H323_G711Capability::H323_G711Capability(Mode m, Speed s)
  : H323AudioCapability(240, 30) // 240ms max, 30ms desired
{
  mode = m;
  speed = s;
}


PObject * H323_G711Capability::Clone() const
{
  return new H323_G711Capability(*this);
}


unsigned H323_G711Capability::GetSubType() const
{
  static const unsigned G711SubType[2][2] = {
    { H245_AudioCapability::e_g711Alaw64k, H245_AudioCapability::e_g711Alaw56k },
    { H245_AudioCapability::e_g711Ulaw64k, H245_AudioCapability::e_g711Ulaw56k }
  };
  return G711SubType[mode][speed];
}


PString H323_G711Capability::GetFormatName() const
{
  static const char * const G711Name[2][2] = {
    { OPAL_G711_ALAW_64K, OPAL_G711_ALAW_56K },
    { OPAL_G711_ULAW_64K, OPAL_G711_ULAW_56K },
  };
  return G711Name[mode][speed];
}


H323Codec * H323_G711Capability::CreateCodec(H323Codec::Direction direction) const
{
  unsigned packetSize = 8*(direction == H323Codec::Encoder ? txFramesInPacket : rxFramesInPacket);

  if (mode == muLaw)
    return new H323_muLawCodec(direction, speed, packetSize);
  else
    return new H323_ALawCodec(direction, speed, packetSize);
}

#endif // H323AudioCodec


/////////////////////////////////////////////////////////////////////////////

char OpalUserInputRFC2833[] = "UserInput/RFC2833";

#ifdef H323_H249
// H.249 Identifiers
const char * const H323_UserInputCapability::SubTypeOID[4] = {
    "0.0.8.1",  // H.249 Annex A
    "0.0.8.2",  // H.249 Annex B
    "0.0.8.3",  // H.249 Annex C
    "0.0.8.4"   // H.249 Annex D
};
#endif

const char * const H323_UserInputCapability::SubTypeNames[NumSubTypes] = {
  "UserInput/basicString",
  "UserInput/iA5String",
  "UserInput/generalString",
  "UserInput/dtmf",
  "UserInput/hookflash",
  OpalUserInputRFC2833      // "UserInput/RFC2833"
#ifdef H323_H249
  ,"UserInput/Navigation",
  "UserInput/Softkey",
  "UserInput/PointDevice",
  "UserInput/Modal"
#endif
};

OPAL_MEDIA_FORMAT_DECLARE(OpalUserInputRFC2833Format,
        OpalUserInputRFC2833,
        OpalMediaFormat::DefaultAudioSessionID,
        (RTP_DataFrame::PayloadTypes)101, // Choose this for Cisco IOS compatibility
        TRUE,   // Needs jitter
        100,    // bits/sec
        4,      // bytes/frame
        8*150,  // 150 millisecond
        OpalMediaFormat::AudioTimeUnits,
        0)

H323_UserInputCapability::H323_UserInputCapability(SubTypes _subType)
{
  subType = _subType;
  
#ifdef H323_H249
  if (subType > 5)
       subTypeOID = SubTypeOID[subType-6];
  else {
#endif
  
    OpalMediaFormat * fmt = OpalMediaFormatFactory::CreateInstance(OpalUserInputRFC2833);
    if (fmt != NULL)
      rtpPayloadType = fmt->GetPayloadType();
      
#ifdef H323_H249
	subTypeOID = PString();
  }
#endif
}


PObject * H323_UserInputCapability::Clone() const
{
  return new H323_UserInputCapability(*this);
}


H323Capability::MainTypes H323_UserInputCapability::GetMainType() const
{
  return e_UserInput;
}


#define SignalToneRFC2833_SubType 10000

static unsigned UserInputCapabilitySubTypeCodes[] = {
  H245_UserInputCapability::e_basicString,
  H245_UserInputCapability::e_iA5String,
  H245_UserInputCapability::e_generalString,
  H245_UserInputCapability::e_dtmf,
  H245_UserInputCapability::e_hookflash,
  SignalToneRFC2833_SubType
#ifdef H323_H249
  ,H245_UserInputCapability::e_genericUserInputCapability,  // H.249 Annex A
  H245_UserInputCapability::e_genericUserInputCapability,  // H.249 Annex B
  H245_UserInputCapability::e_genericUserInputCapability,  // H.249 Annex C
  H245_UserInputCapability::e_genericUserInputCapability   // H.249 Annex D
#endif
};

unsigned  H323_UserInputCapability::GetSubType()  const
{
  return UserInputCapabilitySubTypeCodes[subType];
}


PString H323_UserInputCapability::GetFormatName() const
{
  return SubTypeNames[subType];
}


H323Channel * H323_UserInputCapability::CreateChannel(H323Connection &,
                                                      H323Channel::Directions,
                                                      unsigned,
                                                      const H245_H2250LogicalChannelParameters *) const
{
  PTRACE(1, "Codec\tCannot create UserInputCapability channel");
  return NULL;
}


H323Codec * H323_UserInputCapability::CreateCodec(H323Codec::Direction) const
{
  PTRACE(1, "Codec\tCannot create UserInputCapability codec");
  return NULL;
}

#ifdef H323_H249
H245_GenericInformation * H323_UserInputCapability::BuildGenericIndication(const char * oid)
{
  H245_GenericInformation * cap = new H245_GenericInformation();
  cap->IncludeOptionalField(H245_GenericMessage::e_subMessageIdentifier);

  H245_CapabilityIdentifier & id = cap->m_messageIdentifier;
  id.SetTag(H245_CapabilityIdentifier::e_standard);
  PASN_ObjectId & gid = id;
  gid.SetValue(oid);
  return cap;
}

H245_GenericParameter * H323_UserInputCapability::BuildGenericParameter(unsigned id,unsigned type, const PString & value)
{  

 H245_GenericParameter * content = new H245_GenericParameter();
      H245_ParameterIdentifier & paramid = content->m_parameterIdentifier;
        paramid.SetTag(H245_ParameterIdentifier::e_standard);
        PASN_Integer & pid = paramid;
        pid.SetValue(id);
	  H245_ParameterValue & paramval = content->m_parameterValue;
	    paramval.SetTag(type);
		 if ((type == H245_ParameterValue::e_unsignedMin) ||
			(type == H245_ParameterValue::e_unsignedMax) ||
			(type == H245_ParameterValue::e_unsigned32Min) ||
			(type == H245_ParameterValue::e_unsigned32Max)) {
				PASN_Integer & val = paramval;
				val.SetValue(value.AsUnsigned());
		 } else if (type == H245_ParameterValue::e_octetString) {
				PASN_OctetString & val = paramval;
				val.SetValue(value);
		 }		
//			H245_ParameterValue::e_logical,
//			H245_ParameterValue::e_booleanArray,
//			H245_ParameterValue::e_genericParameter

     return content;
}
#endif

BOOL H323_UserInputCapability::OnSendingPDU(H245_Capability & pdu) const
{
  if (subType == SignalToneRFC2833) {
    pdu.SetTag(H245_Capability::e_receiveRTPAudioTelephonyEventCapability);
    H245_AudioTelephonyEventCapability & atec = pdu;
    atec.m_dynamicRTPPayloadType = rtpPayloadType;
    atec.m_audioTelephoneEvent = "0-16"; // Support DTMF 0-9,*,#,A-D & hookflash
  }
  else {
    pdu.SetTag(H245_Capability::e_receiveUserInputCapability);
    H245_UserInputCapability & ui = pdu;
    ui.SetTag(UserInputCapabilitySubTypeCodes[subType]);

#ifdef H323_H249
	// H.249 Capabilities
	if (subType > 5) {
		H245_GenericCapability & generic = ui;
		H245_CapabilityIdentifier & id = generic.m_capabilityIdentifier;
        id.SetTag(H245_CapabilityIdentifier::e_standard);
		PASN_ObjectId & oid = id;
		oid.SetValue(subTypeOID);

		if (subType == H249B_Softkey) {
			H245_ArrayOf_GenericParameter & col = generic.m_collapsing;
			// Set this to 10 so to support either 2 or 5
		    H245_GenericParameter * param = 
			       BuildGenericParameter(1,H245_ParameterValue::e_unsignedMin,10); 
		  col.Append(param);  
		  col.SetSize(col.GetSize()+1);
	    }
	}
#endif
  }
  return TRUE;
}


BOOL H323_UserInputCapability::OnSendingPDU(H245_DataType &) const
{
  PTRACE(1, "Codec\tCannot have UserInputCapability in DataType");
  return FALSE;
}


BOOL H323_UserInputCapability::OnSendingPDU(H245_ModeElement &) const
{
  PTRACE(1, "Codec\tCannot have UserInputCapability in ModeElement");
  return FALSE;
}


BOOL H323_UserInputCapability::OnReceivedPDU(const H245_Capability & pdu)
{
  H323Capability::OnReceivedPDU(pdu);

  if (pdu.GetTag() == H245_Capability::e_receiveRTPAudioTelephonyEventCapability) {
    subType = SignalToneRFC2833;
    const H245_AudioTelephonyEventCapability & atec = pdu;
    rtpPayloadType = (RTP_DataFrame::PayloadTypes)(int)atec.m_dynamicRTPPayloadType;
    // Really should verify atec.m_audioTelephoneEvent here
    return TRUE;
  }

  const H245_UserInputCapability & ui = pdu;
  if (ui.GetTag() != UserInputCapabilitySubTypeCodes[subType])
	  return FALSE;

#ifdef H323_H249
  if (ui.GetTag() == H245_UserInputCapability::e_genericUserInputCapability) {
	  const H245_GenericCapability & generic = ui;
	  const H245_CapabilityIdentifier & id = generic.m_capabilityIdentifier;
      if (!id.GetTag() != H245_CapabilityIdentifier::e_standard)
		  return FALSE;
	  
	  const PASN_ObjectId & oid = id;
	  return (subTypeOID == oid.AsString());
  }
#endif

  return TRUE;

}


BOOL H323_UserInputCapability::OnReceivedPDU(const H245_DataType &, BOOL)
{
  PTRACE(1, "Codec\tCannot have UserInputCapability in DataType");
  return FALSE;
}


BOOL H323_UserInputCapability::IsUsable(const H323Connection & connection) const
{
  if (connection.GetControlVersion() >= 7)
    return TRUE;

  if (connection.GetRemoteApplication().Find("AltiServ-ITG") != P_MAX_INDEX)
    return FALSE;

  return subType != SignalToneRFC2833;
}


void H323_UserInputCapability::AddAllCapabilities(H323Capabilities & capabilities,
                                                  PINDEX descriptorNum,
                                                  PINDEX simultaneous)
{
  PINDEX num = capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(HookFlashH245));
  if (descriptorNum == P_MAX_INDEX) {
    descriptorNum = num;
    simultaneous = P_MAX_INDEX;
  }
  else if (simultaneous == P_MAX_INDEX)
    simultaneous = num+1;

  num = capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(BasicString));
  if (simultaneous == P_MAX_INDEX)
    simultaneous = num;

  capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(SignalToneH245));
  capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(SignalToneRFC2833));

#ifdef H323_H249
//// H.249 Capabilities
  capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(H249A_Navigation));
  capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(H249B_Softkey));
  capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(H249C_PointDevice));
  capabilities.SetCapability(descriptorNum, simultaneous, new H323_UserInputCapability(H249D_Modal));
#endif
}


/////////////////////////////////////////////////////////////////////////////

BOOL H323SimultaneousCapabilities::SetSize(PINDEX newSize)
{
  PINDEX oldSize = GetSize();

  if (!H323CapabilitiesListArray::SetSize(newSize))
    return FALSE;

  while (oldSize < newSize) {
    H323CapabilitiesList * list = new H323CapabilitiesList;
    // The lowest level list should not delete codecs on destruction
    list->DisallowDeleteObjects();
    SetAt(oldSize++, list);
  }

  return TRUE;
}


BOOL H323CapabilitiesSet::SetSize(PINDEX newSize)
{
  PINDEX oldSize = GetSize();

  if (!H323CapabilitiesSetArray::SetSize(newSize))
    return FALSE;

  while (oldSize < newSize)
    SetAt(oldSize++, new H323SimultaneousCapabilities);

  return TRUE;
}


H323Capabilities::H323Capabilities()
{
}


extern int GetH263RemoteMaxCapabilities( H245_H263VideoCapability & gc );
extern void GetRemoteMaxCapabilities( int level, int maxMBPS, int maxFS, int &w, int &h, int &r, int &f );

void findBestCapabilities(const H245_ArrayOf_CapabilityTableEntry &capabilityTable, int &h264Index, int &h263ExtIndex) {
	
  int h264MaxRes=0;
  int h263ExtMaxRes=0;
  
  for (PINDEX i = 0; i < capabilityTable.GetSize(); i++) {
	
    if (!capabilityTable[i].HasOptionalField(H245_CapabilityTableEntry::e_capability))
      continue;
    
    H245_Capability & m_capability = capabilityTable[i].m_capability;
	
    if (m_capability.GetTag() != H245_Capability::e_receiveVideoCapability && 
        m_capability.GetTag() != H245_Capability::e_receiveAndTransmitVideoCapability)
      continue;
    
    H245_VideoCapability & vc = (H245_VideoCapability &)m_capability;
	
    if (vc.GetTag()==H245_VideoCapability::e_genericVideoCapability) {
      
      H245_GenericCapability & gc = (H245_GenericCapability &)vc;
      
      if (gc.m_capabilityIdentifier.GetTag() != H245_CapabilityIdentifier::e_standard || !gc.HasOptionalField(H245_GenericCapability::e_collapsing))
        continue;
      
      int profile = 0;
      int level = 0;
      int maxMBPS = 0;
      int maxFS = 0;
      for (int j=0; j<gc.m_collapsing.GetSize(); j++) {
		
        H245_GenericParameter & p = gc.m_collapsing[j];
		
        if (p.m_parameterIdentifier.GetTag()!=H245_ParameterIdentifier::e_standard)
          continue;
        
        int id = (int)(PASN_Integer &)p.m_parameterIdentifier;
        int val = (int)(PASN_Integer &)p.m_parameterValue;
		
        if (id==41)
          profile=val;
        else if (id==42)
          level=val;
        else if (id==3)
          maxMBPS=val;
        else if (id==4)
          maxFS=val;
        
      }
      
      if (profile==0)
        continue;
      
      int width=0;
      int height=0;
      int rate=0;
      int fps=0;
      GetRemoteMaxCapabilities ( level, maxMBPS, maxFS, width, height, rate, fps);
      
      int res = width*height;
      PTRACE( 3, "cap[" << i <<"]: " << width << " x " << height << " res=" << res );
      if (res>h264MaxRes) {
        h264MaxRes=res;
        h264Index=i;
      }
			
    }
	
    if (vc.GetTag()==H245_VideoCapability::e_extendedVideoCapability) {
      H245_ExtendedVideoCapability & ec = (H245_ExtendedVideoCapability &) vc;
      if (ec.m_videoCapability.GetSize()) {
        H245_VideoCapability & vc = ec.m_videoCapability[0];
        if (vc.GetTag()==H245_VideoCapability::e_h263VideoCapability) {
          int res=GetH263RemoteMaxCapabilities((H245_H263VideoCapability &)vc);
          if (res>h263ExtMaxRes) {
            h263ExtMaxRes=res;
            h263ExtIndex=i;
          }
        }
      }
    }
  }

}

int GetH263RemoteMaxCapabilities( H245_H263VideoCapability & gc ) {
	
	int maxRes=0;
	
	for (int i=0,res=1; i<5; i++,res*=2) {
		int mpiEnum = (i==0 ? H245_H263VideoCapability::e_sqcifMPI :
					   i==1 ? H245_H263VideoCapability::e_qcifMPI :
					   i==2 ? H245_H263VideoCapability::e_cifMPI :
					   i==3 ? H245_H263VideoCapability::e_cif4MPI :
					   i==4 ? H245_H263VideoCapability::e_cif16MPI :
					   0);
		if (gc.HasOptionalField(mpiEnum)) {
			int w = res * 352 / 4;
			int h = res * 288 / 4;
			int res = w*h;
			if (res>maxRes)
				maxRes=res;
		}
	}
	
	if (gc.HasOptionalField(H245_H263VideoCapability::e_h263Options) &&
		gc.m_h263Options.HasOptionalField(H245_H263Options::e_customPictureFormat) &&
		gc.m_h263Options.m_customPictureFormat.GetSize())
	{
		H245_ArrayOf_CustomPictureFormat & m_customPictureFormat = gc.m_h263Options.m_customPictureFormat;
		for (int i=0; i<m_customPictureFormat.GetSize(); i++) {
			H245_CustomPictureFormat & cpf = m_customPictureFormat[i];
			int w = (int)cpf.m_maxCustomPictureWidth * 4;
			int h = (int)cpf.m_maxCustomPictureHeight * 4;
			int res = w*h;
			if (res>maxRes)
				maxRes=res;
		}
	}
	
	return maxRes;
	
}

#define STRCMPI	strcasecmp
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

void GetRemoteMaxCapabilities( int level, int maxMBPS, int maxFS, int &w, int &h, int &r, int &f ) {
	
  PTRACE( 3, " GetRemoteMaxCapabilities: in level=" << level << " maxMBPS=" << maxMBPS << " maxFS=" << maxFS << " fps=" << f );
	
  if ( f == 0 )
    f=30; // we have to assume 30 fps because that's the max that the app may use
  //default is sqcif 64kbps
  w=128;
  h=96;
  r=64;


  //refer to H.241
  switch ( level ) {
  case 15:                                              // level 1
    if ( maxFS == 0 )
      maxFS = 1;
    w = 128; h = 96; r = 64; break;
  case 19:                                              //level 1b
    if ( maxFS == 0 )
      maxFS = 1;
    w = 128; h = 96; r = 128; break;
  case 22:                                              //level 1.1
    if ( maxFS == 0 )
      maxFS = 1;
    w = 176; h = 144; r = 192; break;
  case 29:                                              //level 1.2
    if ( maxFS == 0 )
      maxFS = 1;
    w = 176; h = 144; r = 384; break;
  case 36:                                              //level 1.3
  case 43:                                             //level 2 
  case 50:                                              //level 2.1 
  case 57:                                              //level 2.2
    if ( maxFS == 0 )
      maxFS = 3;
    w = 352; h = 288; r = 384; break;
  case 64:     
    if ( maxFS == 0 )
      maxFS = 7; // ~ 1620 / 256                                         //level 3 ?
    w = 720; h = 480; r = 768; break;
  case 71:                                              //level 3.1
  case 78:                                             //level 3.2
    if ( maxFS == 0 )
      maxFS = 15 ;  // ~ 3600 /256
    w = 1280; h = 720; r = 1600; break;
  default:                                              //level 4 and higher
    if ( maxFS == 0 )
      maxFS = 8192 /256;
    w = 1920; h = 1080;	r = 2048; break;
  }
	
  // can we send higher resolution than specified by the level?
  if ( maxMBPS ) {
    if ( maxMBPS >= 486 ) {  // 1080P 486=243000/500  
      w = 1920; h = 1080; r = 2048;
      PTRACE( 2, "GetRemoteMaxCapabilities: maxMBPS > 486  w=" << w << " h=" << h << " r=" << r << " f=" << f );
    }
    else if ( maxMBPS >= 216 ) {  // assume 16 x 9 at HD and above 720P 216=108000/500 
      w = 1280; h = 720; r = 1600;
      PTRACE( 2, "GetRemoteMaxCapabilities: maxMBPS > 216  w=" << w << " h=" << h << " r=" << r << " f=" << f );
    }
    else {
      int mbPerFrame = maxMBPS * 500 / f;
      w = sqrt( mbPerFrame * 4 / 3 );     	// get largest width possible for 4x3 aspect ratio - in MB's
      h = w * 3 / 4;           			// calculate height
      w *= 16;                                  // convert to pixels
      h *= 16;
      PTRACE( 2, "GetRemoteMaxCapabilities: maxMBPS < 216  w=" << w << " h=" << h << " r=" << r << " f=" << f );
    }
    if ( f != 30 ) {
      PTRACE( 2, "GetRemoteMaxCapabilities: adjusted from f!=30 w=" << w << " h=" << h << " r=" << r << " f=" << f );
      w = MIN(w * (30 / f), 1920);
      h = MIN(h * (30 / f), 1080);
      PTRACE( 2, "GetRemoteMaxCapabilities: adjusted to   f!=30 w=" << w << " h=" << h << " r=" << r << " f=" << f );
    }
  }


  if ( maxFS ) {                                // did we exceed the max frame size  by applying max MB/s override?
    int maxMbPerFrame = maxFS * 256;
    if ( w * h / 256 > maxMbPerFrame ) {
      if ( maxMbPerFrame >= 8100 ) {
        w = 1920; h = 1080; r =2048;
        PTRACE( 2, "GetRemoteMaxCapabilities: maxMbPerFrame > 8100 default  w=" << w << " h=" << h << " r=" << r << " f=" << f );
      }
      else if ( maxMbPerFrame >= 3600 ) {
        w = 1280; h = 720; r = 1600;
        PTRACE( 2, "GetRemoteMaxCapabilities: maxMbPerFrame > 3600 default  w=" << w << " h=" << h << " r=" << r << " f=" << f );
      }
      else {
        w = sqrt( maxMbPerFrame * 4 / 3 );     	// get largest width possible for 4x3 aspect ratio - in MB's
        h = w * 3 / 4;           			// calculate height
        w *= 16;                                  // convert to pixels
        h *= 16;
        PTRACE( 2, "GetRemoteMaxCapabilities: maxMbPerFrame < 3600 default  w=" << w << " h=" << h << " r=" << r << " f=" << f );
      }
    }
  }
  PTRACE( 2, "GetRemoteMaxCapabilities: out w=" << w << " h=" << h << " r=" << r << " f=" << f );
}

static bool findH235SecurityCapability(const H245_ArrayOf_CapabilityTableEntry &capabilityTable, int mediaCapability) {
	
	for (PINDEX i = 0; i < capabilityTable.GetSize(); i++) {
		
		if (!capabilityTable[i].HasOptionalField(H245_CapabilityTableEntry::e_capability))
			continue;
		
		H245_Capability & m_capability = capabilityTable[i].m_capability;
		
		if (m_capability.GetTag() != H245_Capability::e_h235SecurityCapability)
			continue;
		
		const H245_H235SecurityCapability & sc = m_capability;
		
		if ((int)sc.m_mediaCapability == mediaCapability)
			return true;
		
	}
	
	return false;
	
}

//roni - HDX-4000 cap merge problem

H323Capabilities::H323Capabilities(const H323Connection & connection, const H245_TerminalCapabilitySet & pdu) {
	
  const H323Capabilities & localCapabilities = connection.GetLocalCapabilities();
	
  // Decode out of the PDU, the list of known codecs.
  if (pdu.HasOptionalField(H245_TerminalCapabilitySet::e_capabilityTable)) {
	
    int h264Index=-1;
    int h263ExtIndex=-1;
		
    findBestCapabilities(pdu.m_capabilityTable,h264Index,h263ExtIndex);
		
    PTRACE ( 2, "h264Index = " << h264Index << " h263ExtIndex = " << h263ExtIndex );
		
    for (PINDEX i = 0; i < pdu.m_capabilityTable.GetSize(); i++) {
			
      if (!pdu.m_capabilityTable[i].HasOptionalField(H245_CapabilityTableEntry::e_capability))
        continue;
			
      H245_Capability & m_capability = pdu.m_capabilityTable[i].m_capability;
      H323Capability * capability = localCapabilities.FindCapability(m_capability);
			
      if (capability == NULL)
        continue;
			
      H323Capability *copy = (H323Capability *)capability->Clone();
			
      if (!copy->OnReceivedPDU(m_capability)) {
        delete copy;
        copy=0;
        continue;
      }
			
      copy->SetCapabilityNumber( pdu.m_capabilityTable[i].m_capabilityTableEntryNumber );
      
      H323Capability *pcap = NULL;
      H245_VideoCapability *pvc = NULL;
      
      if ( m_capability.GetTag() == H245_Capability::e_receiveVideoCapability || m_capability.GetTag() == H245_Capability::e_receiveAndTransmitVideoCapability) {
        H245_VideoCapability &vc = (H245_VideoCapability &)m_capability;
        if ( vc.GetTag() != H245_VideoCapability::e_extendedVideoCapability ) {
          pvc = &vc;
          pcap = copy;
        } 
        else {
          H245_ExtendedVideoCapability &ec = (H245_ExtendedVideoCapability &) vc;
          if (ec.m_videoCapability.GetSize()) {
            pvc = &ec.m_videoCapability[0];
            H323ExtendedVideoCapability & evc = (H323ExtendedVideoCapability &)*copy;
            if (evc.GetSize())
              pcap = &evc[0];
          }
        }
      }
      
      if ( pvc && pcap ) {
        H245_VideoCapability &vc = *pvc;
        OpalVideoFormat & fmt = (OpalVideoFormat &)pcap->GetWritableMediaFormat();
        int targetBitRate = 0;
		
        if (vc.GetTag() == H245_VideoCapability::e_h263VideoCapability ) {
          if ( pcap != copy && i != h263ExtIndex ) {
            delete copy;
            copy=0;
            continue;
          }
          
          H245_H263VideoCapability & gc = (H245_H263VideoCapability &)vc;
          targetBitRate = (int)gc.m_maxBitRate*100;
          bool lowbw = (MIN(targetBitRate,capability->GetMediaFormat().GetOptionInteger(OpalVideoFormat::MaxBitRateOption))-64000) <= 128000;
          
          int localMaxRes = capability->GetMediaFormat().GetOptionInteger(OpalVideoFormat::FrameWidthOption) * capability->GetMediaFormat().GetOptionInteger(OpalVideoFormat::FrameHeightOption);
          int remoteMaxRes=0;
          int width=0;
          int height=0;
          
          for (int i=0,res=1; i<5; i++,res*=2) {
            int mpiEnum = (i==0 ? H245_H263VideoCapability::e_sqcifMPI :
                           i==1 ? H245_H263VideoCapability::e_qcifMPI :
                           i==2 ? H245_H263VideoCapability::e_cifMPI :
                           i==3 ? H245_H263VideoCapability::e_cif4MPI :
                           i==4 ? H245_H263VideoCapability::e_cif16MPI :
                           0);
            const char *mpiTag = (i==0 ? "SQCIF MPI" :
                                  i==1 ? "QCIF MPI" :
                                  i==2 ? "CIF MPI" :
                                  i==3 ? "CIF4 MPI" :
                                  i==4 ? "CIF16 MPI" :
                                  0);
            const PASN_Integer & mpi = (i==0 ? gc.m_sqcifMPI :
                                        i==1 ? gc.m_qcifMPI :
                                        i==2 ? gc.m_cifMPI :
                                        i==3 ? gc.m_cif4MPI :
                                        i==4 ? gc.m_cif16MPI :
                                        PASN_Integer(0));
            fmt.SetOptionInteger(mpiTag, gc.HasOptionalField(mpiEnum) ? (int)mpi : 0);
            if (gc.HasOptionalField(mpiEnum)) {
              int w = res * 352 / 4;
              int h = res * 288 / 4;
              int res = w*h;
              if (res>remoteMaxRes && res<=localMaxRes) {
                remoteMaxRes=res;
                width=w;
                height=h;
                if (lowbw && i==1)
                  break;
              }
            }
          }
          
          if (gc.HasOptionalField(H245_H263VideoCapability::e_h263Options)) {
            char *ptr;
            int i = asprintf(&ptr,"%p",(H245_H263Options *)gc.m_h263Options.Clone());
            if ( i != -1 ) {
              fmt.AddOption(new OpalMediaOptionString("h263Options",true,ptr));
              free(ptr);
            }
			
            if ( pcap != copy && gc.m_h263Options.HasOptionalField(H245_H263Options::e_customPictureFormat) && gc.m_h263Options.m_customPictureFormat.GetSize()) {
              H245_ArrayOf_CustomPictureFormat & m_customPictureFormat = gc.m_h263Options.m_customPictureFormat;
              for (int i=0; i<m_customPictureFormat.GetSize(); i++) {
                H245_CustomPictureFormat & cpf = m_customPictureFormat[i];
                int w = (int)cpf.m_maxCustomPictureWidth * 4;
                int h = (int)cpf.m_maxCustomPictureHeight * 4;
                int res = w*h;
                if ( res > remoteMaxRes && res <= localMaxRes ) {
                  remoteMaxRes = res;
                  width = w;
                  height = h;
                }
              }
            }
          }
          
          fmt.SetOptionInteger(OpalVideoFormat::FrameWidthOption,width);
          fmt.SetOptionInteger(OpalVideoFormat::FrameHeightOption,height);
        }
		
        if ( vc.GetTag() == H245_VideoCapability::e_genericVideoCapability ) {
          if ( pcap == copy && i != h264Index) {
            delete copy;
            copy=0;
            continue;
          }
          
          H245_GenericCapability & gc = (H245_GenericCapability &)vc;
          targetBitRate = (int)gc.m_maxBitRate*100;
          
          if (gc.m_capabilityIdentifier.GetTag() == H245_CapabilityIdentifier::e_standard && gc.HasOptionalField(H245_GenericCapability::e_collapsing)) {
            while (true) {
              bool found=false;
              for (int i=0; i<fmt.GetOptionCount(); i++) {
                OpalMediaOption & opt = fmt.GetOption(i);
                if (opt.GetName().Find("Generic Parameter ")!=P_MAX_INDEX) {
                  PTRACE ( 2, "Removed " << (const char*)opt.GetName() << " " <<  (const char*)opt.AsString());
                  fmt.RemoveOption(&opt);
                  found=true;
                  break;
                }
              }
              if (!found)
                break;
            }
            
            int level = 0;
            int maxMBPS = 0;
            int maxFS = 0;
            for (int j=0; j<gc.m_collapsing.GetSize(); j++) {
              
              H245_GenericParameter & p = gc.m_collapsing[j];
              
              if (p.m_parameterIdentifier.GetTag()!=H245_ParameterIdentifier::e_standard)
                continue;
              
              unsigned int id = (int)(PASN_Integer &)p.m_parameterIdentifier;
              int val = (int)(PASN_Integer &)p.m_parameterValue;
              
              if (id==3)
                maxMBPS=val;
              else if (id==4)
                maxFS=val;
              else if (id==42)
                level=val;
              
              OpalMediaOption::H245GenericInfo::IntegerTypes type;
              OpalMediaOption::MergeType merge;
              
              const PString & tagName = p.m_parameterValue.GetTagName();
              if (tagName.Find("unsigned")!=P_MAX_INDEX) {
                type = tagName.Find("32")==P_MAX_INDEX ? OpalMediaOption::H245GenericInfo::UnsignedInt : OpalMediaOption::H245GenericInfo::Unsigned32;
                merge = tagName.Find("Max")==P_MAX_INDEX ? OpalMediaOption::MinMerge : OpalMediaOption::MaxMerge;
              } 
              else if (tagName.Find("booleanArray")!=P_MAX_INDEX) {
                type = OpalMediaOption::H245GenericInfo::BooleanArray;
                merge = OpalMediaOption::NoMerge;
              } 
              else
                continue;
              
              PString name(PString::Printf,"Generic Parameter %d",id);
              OpalMediaOption *opt = new OpalMediaOptionUnsigned(name,true,merge,val);
              OpalMediaOption::H245GenericInfo gi = {id,OpalMediaOption::H245GenericInfo::Collapsing,type,0,0,0};
              opt->SetH245Generic(gi);
              fmt.AddOption(opt,true);
              PTRACE( 2, "Added " << (const char*)opt->GetName() << " " << (const char*)opt->AsString());
            }
			
            if (connection.GetRemoteApplication().Find("Polycom RMX 2000")!=P_MAX_INDEX) {
                if (connection.getOldRmx() == 1 ) {
                    PTRACE( 2, "Detected RMX - setting maxMBPS to 0 ");
                    maxMBPS=0;
                }
                else
                    PTRACE( 2, "Detected RMX - oldRmx not set so do nothing ");
			}
        
            int width=0;
            int height=0;
            int rate = 0;
            int fps = 0;
      
            /* MXP can actually decode 720P but does not advertise that in the caps  */
            if (connection.GetRemoteApplication().Find("Tandberg MXP")!=P_MAX_INDEX) 
            {
                if (connection.getmxp720p()) {
                    PTRACE( 2, "Detected Mxp - mxp720p flag SET - setting maxMBPS to HD ");
                    maxMBPS=216;
                }
                else
                    PTRACE( 2, "Detected Mxp - mxp720p flag NOT set ");
            }
        
	    /* h239 capability */
	    if (pcap!=copy)
            fps = 5;

            GetRemoteMaxCapabilities ( level, maxMBPS, maxFS, width, height, rate, fps );
            fmt.AddOption(new OpalMediaOptionInteger("maxMBPS",true,OpalMediaOption::NoMerge,maxMBPS));
            
            if (targetBitRate <= 0)
              targetBitRate=rate*1000;
            
            width = MIN(width,fmt.GetOptionInteger(OpalVideoFormat::FrameWidthOption));
            height = MIN(height,fmt.GetOptionInteger(OpalVideoFormat::FrameHeightOption));
            fmt.SetOptionInteger(OpalVideoFormat::FrameWidthOption,width);
            fmt.SetOptionInteger(OpalVideoFormat::FrameHeightOption,height);
            
          }
          else {
            delete copy;
            copy=0;
            continue;
          }
        }
		
        fmt.SetOptionInteger(OpalVideoFormat::MaxBitRateOption, MIN(targetBitRate,capability->GetMediaFormat().GetOptionInteger(OpalVideoFormat::MaxBitRateOption))-64000);
      }
      
      H245_CapabilityTableEntry & m_capabilityTableEntry = pdu.m_capabilityTable[i];
      
      if (((H323Connection &)connection).getSharedSecret().GetSize()>0 && findH235SecurityCapability(pdu.m_capabilityTable,(int)m_capabilityTableEntry.m_capabilityTableEntryNumber)) {
        OpalMediaFormat &fmt = copy->GetWritableMediaFormat();
        const char *name = "MediaEncryptionAlgorithm";
        if (fmt.HasOption(name))
          fmt.RemoveOption(fmt.FindOption(name));
        fmt.AddOption(new OpalMediaOptionString(name,true,connection.getMediaEncryptionAlgorithm()));
        if (copy->GetFormatName()=="H.224")
          ((H323DataCapability *)copy)->getMediaEncryptionAlgorithm()=connection.getMediaEncryptionAlgorithm();
      }
      
      table.Append(copy);
    }
  }
	
  PINDEX outerSize = pdu.m_capabilityDescriptors.GetSize();
  set.SetSize(outerSize);
  for (PINDEX outer = 0; outer < outerSize; outer++) {
    H245_CapabilityDescriptor & desc = pdu.m_capabilityDescriptors[outer];
    if (desc.HasOptionalField(H245_CapabilityDescriptor::e_simultaneousCapabilities)) {
      PINDEX middleSize = desc.m_simultaneousCapabilities.GetSize();
      set[outer].SetSize(middleSize);
      for (PINDEX middle = 0; middle < middleSize; middle++) {
        H245_AlternativeCapabilitySet & alt = desc.m_simultaneousCapabilities[middle];
        for (PINDEX inner = 0; inner < alt.GetSize(); inner++) {
          for (PINDEX cap = 0; cap < table.GetSize(); cap++) {
            if (table[cap].GetCapabilityNumber() == alt[inner]) {
              set[outer][middle].Append(&table[cap]);
              break;
            }
          }
        }
      }
    }
  }
	
  PStringArray preferenceOrder;
  preferenceOrder.AppendString("H.264-H.239*");
  preferenceOrder.AppendString("H.263-H.239*");
  (*this).Reorder(preferenceOrder);
}


H323Capabilities::H323Capabilities(const H323Capabilities & original)
{
  operator=(original);
}


H323Capabilities & H323Capabilities::operator=(const H323Capabilities & original)
{
  RemoveAll();

  for (PINDEX i = 0; i < original.GetSize(); i++)
    Copy(original[i]);

  PINDEX outerSize = original.set.GetSize();
  set.SetSize(outerSize);
  for (PINDEX outer = 0; outer < outerSize; outer++) {
    PINDEX middleSize = original.set[outer].GetSize();
    set[outer].SetSize(middleSize);
    for (PINDEX middle = 0; middle < middleSize; middle++) {
      PINDEX innerSize = original.set[outer][middle].GetSize();
      for (PINDEX inner = 0; inner < innerSize; inner++)
        set[outer][middle].Append(FindCapability(original.set[outer][middle][inner].GetCapabilityNumber()));
    }
  }

  return *this;
}


void H323Capabilities::PrintOn(ostream & strm) const
{
  int indent = strm.precision()-1;
  strm << setw(indent) << " " << "Table:\n";
  for (PINDEX i = 0; i < table.GetSize(); i++)
    strm << setw(indent+2) << " " << table[i] << '\n';

  strm << setw(indent) << " " << "Set:\n";
  for (PINDEX outer = 0; outer < set.GetSize(); outer++) {
    strm << setw(indent+2) << " " << outer << ":\n";
    for (PINDEX middle = 0; middle < set[outer].GetSize(); middle++) {
      strm << setw(indent+4) << " " << middle << ":\n";
      for (PINDEX inner = 0; inner < set[outer][middle].GetSize(); inner++)
        strm << setw(indent+6) << " " << set[outer][middle][inner] << '\n';
    }
  }
}


PINDEX H323Capabilities::SetCapability(PINDEX descriptorNum,
                                       PINDEX simultaneousNum,
                                       H323Capability * capability)
{
  if (capability == NULL)
    return P_MAX_INDEX;

  // Make sure capability has been added to table.
  Add(capability);

  BOOL newDescriptor = descriptorNum == P_MAX_INDEX;
  if (newDescriptor)
    descriptorNum = set.GetSize();

  // Make sure the outer array is big enough
  set.SetMinSize(descriptorNum+1);

  if (simultaneousNum == P_MAX_INDEX)
    simultaneousNum = set[descriptorNum].GetSize();

  // Make sure the middle array is big enough
  set[descriptorNum].SetMinSize(simultaneousNum+1);

  // Now we can put the new entry in.
  set[descriptorNum][simultaneousNum].Append(capability);
  return newDescriptor ? descriptorNum : simultaneousNum;
}


static BOOL MatchWildcard(const PCaselessString & str, const PStringArray & wildcard)
{
  PINDEX last = 0;
  for (PINDEX i = 0; i < wildcard.GetSize(); i++) {
    if (wildcard[i].IsEmpty())
      last = str.GetLength();
    else {
      PINDEX next = str.Find(wildcard[i], last);
      if (next == P_MAX_INDEX)
        return FALSE;
      last = next + wildcard[i].GetLength();
    }
  }

  return TRUE;
}


PINDEX H323Capabilities::AddAllCapabilities(PINDEX descriptorNum,
                                            PINDEX simultaneous,
                                            const PString & name)
{
  PINDEX reply = descriptorNum == P_MAX_INDEX ? P_MAX_INDEX : simultaneous;

  PStringArray wildcard = name.Tokenise('*', FALSE);

  H323CapabilityFactory::KeyList_T stdCaps = H323CapabilityFactory::GetKeyList();

  for (unsigned session = OpalMediaFormat::FirstSessionID; session <= OpalMediaFormat::LastSessionID; session++) {
    for (H323CapabilityFactory::KeyList_T::const_iterator r = stdCaps.begin(); r != stdCaps.end(); ++r) {
      PString capName(*r);
      if (MatchWildcard(capName, wildcard) && (FindCapability(capName) == NULL)) {
        OpalMediaFormat mediaFormat(capName);
        if (!mediaFormat.IsValid() && (capName.Right(4) == "{sw}") && capName.GetLength() > 4)
          mediaFormat = OpalMediaFormat(capName.Left(capName.GetLength()-4));
        if (mediaFormat.IsValid() && mediaFormat.GetDefaultSessionID() == session) {
          // add the capability
          H323Capability * capability = H323Capability::Create(capName);
          PINDEX num = SetCapability(descriptorNum, simultaneous, capability);
          if (descriptorNum == P_MAX_INDEX) {
            reply = num;
            descriptorNum = num;
            simultaneous = P_MAX_INDEX;
          }
          else if (simultaneous == P_MAX_INDEX) {
            if (reply == P_MAX_INDEX)
              reply = num;
            simultaneous = num;
          }
        }
      }
    }
    //simultaneous = P_MAX_INDEX;
  }

  return reply;
}


static unsigned MergeCapabilityNumber(const H323CapabilitiesList & table,
                                      unsigned newCapabilityNumber)
{
  // Assign a unique number to the codec, check if the user wants a specific
  // value and start with that.
  if (newCapabilityNumber == 0)
    newCapabilityNumber = 1;

  PINDEX i = 0;
  while (i < table.GetSize()) {
    if (table[i].GetCapabilityNumber() != newCapabilityNumber)
      i++;
    else {
      // If it already in use, increment it
      newCapabilityNumber++;
      i = 0;
    }
  }

  return newCapabilityNumber;
}


void H323Capabilities::Add(H323Capability * capability)
{
  if (capability == NULL)
    return;

  // See if already added, confuses things if you add the same instance twice
  if (table.GetObjectsIndex(capability) != P_MAX_INDEX)
    return;

  capability->SetCapabilityNumber(MergeCapabilityNumber(table, 1));
  table.Append(capability);

  PTRACE(3, "H323\tAdded capability: " << *capability);
}


H323Capability * H323Capabilities::Copy(const H323Capability & capability)
{
  H323Capability * newCapability = (H323Capability *)capability.Clone();
  newCapability->SetCapabilityNumber(MergeCapabilityNumber(table, capability.GetCapabilityNumber()));
  table.Append(newCapability);

  PTRACE(3, "H323\tAdded capability: " << *newCapability);
  return newCapability;
}


void H323Capabilities::Remove(H323Capability * capability)
{
  if (capability == NULL)
    return;

  PTRACE(3, "H323\tRemoving capability: " << *capability);

  unsigned capabilityNumber = capability->GetCapabilityNumber();

  for (PINDEX outer = 0; outer < set.GetSize(); outer++) {
    for (PINDEX middle = 0; middle < set[outer].GetSize(); middle++) {
      for (PINDEX inner = 0; inner < set[outer][middle].GetSize(); inner++) {
        if (set[outer][middle][inner].GetCapabilityNumber() == capabilityNumber) {
          set[outer][middle].RemoveAt(inner);
          break;
        }
      }
      if (set[outer][middle].GetSize() == 0)
        set[outer].RemoveAt(middle);
    }
    if (set[outer].GetSize() == 0)
      set.RemoveAt(outer);
  }

  table.Remove(capability);
}


void H323Capabilities::Remove(const PString & codecName)
{
  if (codecName.IsEmpty())
    return;

  H323Capability * cap = FindCapability(codecName);
  while (cap != NULL) {
    Remove(cap);
    cap = FindCapability(codecName);
  }
}


void H323Capabilities::Remove(const PStringArray & codecNames)
{
  for (PINDEX i = 0; i < codecNames.GetSize(); i++)
    Remove(codecNames[i]);
}


void H323Capabilities::RemoveAll()
{
  table.RemoveAll();
  set.RemoveAll();
}


H323Capability * H323Capabilities::FindCapability(unsigned capabilityNumber) const
{
  PTRACE(3, "H323\tFindCapability: " << capabilityNumber);

  for (PINDEX i = 0; i < table.GetSize(); i++) {
    if (table[i].GetCapabilityNumber() == capabilityNumber) {
      PTRACE(3, "H323\tFound capability: " << table[i]);
      return &table[i];
    }
  }

  return NULL;
}


H323Capability * H323Capabilities::FindCapability(const PString & formatName,
                              H323Capability::CapabilityDirection direction) const
{
  PTRACE(3, "H323\tFindCapability: \"" << formatName << '"');

  PStringArray wildcard = formatName.Tokenise('*', FALSE);

  for (PINDEX i = 0; i < table.GetSize(); i++) {
    PCaselessString str = table[i].GetFormatName();
    if (MatchWildcard(str, wildcard) &&
          (direction == H323Capability::e_Unknown ||
           table[i].GetCapabilityDirection() == direction)) {
      PTRACE(3, "H323\tFound capability: " << table[i]);
      return &table[i];
    }
  }

  return NULL;
}


H323Capability * H323Capabilities::FindCapability(
                              H323Capability::CapabilityDirection direction) const
{
  PTRACE(3, "H323\tFindCapability: \"" << direction << '"');

  for (PINDEX i = 0; i < table.GetSize(); i++) {
    if (table[i].GetCapabilityDirection() == direction) {
      PTRACE(3, "H323\tFound capability: " << table[i]);
      return &table[i];
    }
  }

  return NULL;
}

#define H264_PROFILE "Generic Parameter 41"

PString H323Capability::GetFormatName() const
{
	return "H.323 Capability";
}


H323Capability * H323Capabilities::FindCapability(const H323Capability & capability) const
{
  PTRACE(3, "H323\tFindCapability: " << capability << " main: " << capability.GetMainType() << " sub: " << capability.GetSubType() << " Name: " << capability.GetFormatName() );

  for (PINDEX i = 0; i < table.GetSize(); i++) {
    if (table[i] == capability) {
      if ( capability.GetMainType() == H323Capability::e_Video && capability.GetSubType() == 5 ) { //Roni - find h264 as opposed to rcdo
	//const OpalVideoFormat & local_fmt = (const OpalVideoFormat &)capability.GetMediaFormat();
	//	OpalMediaOption * local_option = local_fmt.FindOption( PString( H264_PROFILE ) );

	OpalVideoFormat & remote_fmt = (OpalVideoFormat &)table[i].GetMediaFormat();
	int profile = remote_fmt.GetOptionInteger( H264_PROFILE );
	//	OpalMediaOption * remote_option = remote_fmt.FindOption( PString( H264_PROFILE ) );


	//	if ( local_option && remote_option && local_option->CompareValue( *remote_option ) == EqualTo ) 
	if ( profile != 0 ) {		//profile = 0 is rcdo
	  PTRACE(3, "H323\tFound capability: " << table[i]);
	  return &table[i];
	}
	else
	  continue;			//this one has a different profile - keep checking
      }
      else {
	PTRACE(3, "H323\tFound capability: " << table[i]);
	return &table[i];
      }
    }
  }

  return NULL;
}


H323Capability * H323Capabilities::FindCapability(const H245_Capability & cap) const
{
  PTRACE(3, "H323\tFindCapability: " << cap.GetTagName());

  switch (cap.GetTag()) {
    case H245_Capability::e_receiveAudioCapability :
    case H245_Capability::e_transmitAudioCapability :
    case H245_Capability::e_receiveAndTransmitAudioCapability :
    {
      const H245_AudioCapability & audio = cap;
      return FindCapability(H323Capability::e_Audio, audio, NULL);
    }

    case H245_Capability::e_receiveVideoCapability :
    case H245_Capability::e_transmitVideoCapability :
    case H245_Capability::e_receiveAndTransmitVideoCapability :
    {
      const H245_VideoCapability & video = cap;
		if (video.GetTag() == H245_VideoCapability::e_extendedVideoCapability) {
			const H245_ExtendedVideoCapability & ec = (H245_ExtendedVideoCapability) video;
			const H245_ArrayOf_VideoCapability & cape = ec.m_videoCapability;
			H323Capability *capability=NULL;
			for (int i=0; i<cape.GetSize() && capability==NULL; i++) {
				const H245_VideoCapability & gcap = cape[i];
				if (gcap.GetTag()==H245_VideoCapability::e_h263VideoCapability)
					capability=FindCapability("H.263-H.239{sw}");
				else if (gcap.GetTag()==H245_VideoCapability::e_genericVideoCapability)
					capability=FindCapability("H.264-H.239{sw}");
			}
			return capability;
		}
		if (video.GetTag() == H245_VideoCapability::e_genericVideoCapability) {
			const H245_GenericCapability & gc = video;
			if (gc.m_capabilityIdentifier.GetTag() == H245_CapabilityIdentifier::e_standard) {
				const PASN_ObjectId & object_id = gc.m_capabilityIdentifier;
				if (object_id.AsString() == OpalPluginCodec_Identifer_H264_SVC)
					return FindCapability("H.264SVC");
			}
		}
      return FindCapability(H323Capability::e_Video, video, NULL);
    }

    case H245_Capability::e_receiveDataApplicationCapability :
    case H245_Capability::e_transmitDataApplicationCapability :
    case H245_Capability::e_receiveAndTransmitDataApplicationCapability :
    {
      const H245_DataApplicationCapability & data = cap;
      return FindCapability(H323Capability::e_Data, data.m_application, NULL);
    }

    case H245_Capability::e_receiveUserInputCapability :
    case H245_Capability::e_transmitUserInputCapability :
    case H245_Capability::e_receiveAndTransmitUserInputCapability :
    {
      const H245_UserInputCapability & ui = cap;
		if (ui.GetTag()==H245_UserInputCapability::e_genericUserInputCapability) {
			const H245_GenericCapability & gc = ui;
			const PASN_ObjectId & val = gc.m_capabilityIdentifier;
			for (int i=0; i<4; i++)
				if (val==H323_UserInputCapability::SubTypeOID[i])
					return FindCapability(H323_UserInputCapability::SubTypeNames[i+6]);
			return NULL;
		}
      return FindCapability(H323Capability::e_UserInput, ui, NULL);
    }

    case H245_Capability::e_receiveRTPAudioTelephonyEventCapability :
      return FindCapability(H323Capability::e_UserInput, SignalToneRFC2833_SubType);

	case H245_Capability::e_genericControlCapability :
	{
	  const H245_GenericCapability & gc = (H245_GenericCapability) cap;
	  if (gc.m_capabilityIdentifier.GetTag() == H245_CapabilityIdentifier::e_standard) {
		  const PASN_ObjectId & object_id = gc.m_capabilityIdentifier;
		  if (object_id.AsString() == OpalPluginCodec_Identifer_H239)
			  return FindCapability("H.239 Control");
	  }
	  return FindCapability(H323Capability::e_GenericControl);
	}

	case H245_Capability::e_conferenceCapability :
	  return FindCapability(H323Capability::e_ConferenceControl);

    default :
      break;
  }

  return NULL;
}


H323Capability * H323Capabilities::FindCapability(const H245_DataType & dataType) const
{
  PTRACE(3, "H323\tFindCapability: " << dataType.GetTagName());

  for (PINDEX i = 0; i < table.GetSize(); i++) {
    H323Capability & capability = table[i];
    BOOL checkExact;
    switch (dataType.GetTag()) {
      case H245_DataType::e_audioData :
      {
        const H245_AudioCapability & audio = dataType;
        checkExact = capability.IsMatch(audio);
        break;
      }

      case H245_DataType::e_videoData :
      {
        const H245_VideoCapability & video = dataType;
        checkExact = capability.IsMatch(video);
        break;
      }

      case H245_DataType::e_data :
      {
        const H245_DataApplicationCapability & data = dataType;
        checkExact = capability.IsMatch(data.m_application);
        break;
      }

      case H245_DataType::e_h235Media :
      {
        const H245_H235Media_mediaType & mediaType = ((const H245_H235Media &)dataType).m_mediaType;
        checkExact = FALSE;
        if (capability.GetMainType()==H323Capability::e_Audio && mediaType.GetTag()==H245_H235Media_mediaType::e_audioData)
			checkExact = capability.IsMatch((const H245_AudioCapability &)mediaType);
		else if (capability.GetMainType()==H323Capability::e_Video && mediaType.GetTag()==H245_H235Media_mediaType::e_videoData)
			checkExact = capability.IsMatch((const H245_VideoCapability &)mediaType);
		else if (capability.GetMainType()==H323Capability::e_Data && mediaType.GetTag()==H245_H235Media_mediaType::e_data)
			checkExact = capability.IsMatch(((const H245_DataApplicationCapability &)mediaType).m_application);
        break;
	  }

      default :
        checkExact = FALSE;
    }

    if (checkExact) {
      H323Capability * compare = (H323Capability *)capability.Clone();
      if (compare->OnReceivedPDU(dataType, FALSE) && *compare == capability) {
        delete compare;
        PTRACE(3, "H323\tFound capability: " << capability);
        return &capability;
      }
      delete compare;
    }
  }

  return NULL;
}


H323Capability * H323Capabilities::FindCapability(const H245_ModeElement & modeElement) const
{
  PTRACE(3, "H323\tFindCapability: " << modeElement.m_type.GetTagName());

  switch (modeElement.m_type.GetTag()) {
    case H245_ModeElementType::e_audioMode :
      {
        const H245_AudioMode & audio = modeElement.m_type;
        static unsigned const AudioSubTypes[] = {
	  H245_AudioCapability::e_nonStandard,
	  H245_AudioCapability::e_g711Alaw64k,
	  H245_AudioCapability::e_g711Alaw56k,
	  H245_AudioCapability::e_g711Ulaw64k,
	  H245_AudioCapability::e_g711Ulaw56k,
	  H245_AudioCapability::e_g722_64k,
	  H245_AudioCapability::e_g722_56k,
	  H245_AudioCapability::e_g722_48k,
	  H245_AudioCapability::e_g728,
	  H245_AudioCapability::e_g729,
	  H245_AudioCapability::e_g729AnnexA,
	  H245_AudioCapability::e_g7231,
	  H245_AudioCapability::e_is11172AudioCapability,
	  H245_AudioCapability::e_is13818AudioCapability,
	  H245_AudioCapability::e_g729wAnnexB,
	  H245_AudioCapability::e_g729AnnexAwAnnexB,
	  H245_AudioCapability::e_g7231AnnexCCapability,
	  H245_AudioCapability::e_gsmFullRate,
	  H245_AudioCapability::e_gsmHalfRate,
	  H245_AudioCapability::e_gsmEnhancedFullRate,
	  H245_AudioCapability::e_genericAudioCapability,
	  H245_AudioCapability::e_g729Extensions
        };
        return FindCapability(H323Capability::e_Audio, audio, AudioSubTypes);
      }

    case H245_ModeElementType::e_videoMode :
      {
        const H245_VideoMode & video = modeElement.m_type;
        static unsigned const VideoSubTypes[] = {
	  H245_VideoCapability::e_nonStandard,
	  H245_VideoCapability::e_h261VideoCapability,
	  H245_VideoCapability::e_h262VideoCapability,
	  H245_VideoCapability::e_h263VideoCapability,
	  H245_VideoCapability::e_is11172VideoCapability,
	  H245_VideoCapability::e_genericVideoCapability
        };
        return FindCapability(H323Capability::e_Video, video, VideoSubTypes);
      }

    case H245_ModeElementType::e_dataMode :
      {
        const H245_DataMode & data = modeElement.m_type;
        static unsigned const DataSubTypes[] = {
	  H245_DataApplicationCapability_application::e_nonStandard,
	  H245_DataApplicationCapability_application::e_t120,
	  H245_DataApplicationCapability_application::e_dsm_cc,
	  H245_DataApplicationCapability_application::e_userData,
	  H245_DataApplicationCapability_application::e_t84,
	  H245_DataApplicationCapability_application::e_t434,
	  H245_DataApplicationCapability_application::e_h224,
	  H245_DataApplicationCapability_application::e_nlpid,
	  H245_DataApplicationCapability_application::e_dsvdControl,
	  H245_DataApplicationCapability_application::e_h222DataPartitioning,
	  H245_DataApplicationCapability_application::e_t30fax,
	  H245_DataApplicationCapability_application::e_t140,
	  H245_DataApplicationCapability_application::e_t38fax,
	  H245_DataApplicationCapability_application::e_genericDataCapability
        };
        return FindCapability(H323Capability::e_Data, data.m_application, DataSubTypes);
      }

    default :
      break;
  }

  return NULL;
}


H323Capability * H323Capabilities::FindCapability(H323Capability::MainTypes mainType,
                                                  const PASN_Choice & subTypePDU,
                                                  const unsigned * translationTable) const
{
    unsigned int subTypeID = subTypePDU.GetTag();
  if (subTypePDU.GetTag() != 0) {
    if (translationTable != NULL)
      subTypeID = translationTable[subTypeID];
    return FindCapability(mainType, subTypeID);
  }

  PTRACE(3, "H323\tFindCapability: " << mainType << " nonStandard");

  for (PINDEX i = 0; i < table.GetSize(); i++) {
    H323Capability & capability = table[i];
    if (capability.IsMatch(subTypePDU)) {
      PTRACE(3, "H323\tFound capability: " << capability);
      return &capability;
    }
  }

  return NULL;
}


H323Capability * H323Capabilities::FindCapability(H323Capability::MainTypes mainType, unsigned subType) const
{
  if (subType != UINT_MAX) {
     PTRACE(3, "H323\tFindCapability: " << mainType << " subtype=" << subType);
  }

  for (PINDEX i = 0; i < table.GetSize(); i++) {
    H323Capability & capability = table[i];
    if (capability.GetMainType() == mainType &&
                        (subType == UINT_MAX || capability.GetSubType() == subType)) {
      PTRACE(3, "H323\tFound capability: " << capability);
      if (subType==H245_VideoCapability::e_genericVideoCapability && capability.GetMediaFormat().Find("SVC")!=P_MAX_INDEX)
        continue;
      return &capability;
    }
  }

  return NULL;
}

BOOL H323Capabilities::RemoveCapability(H323Capability::MainTypes capabilityType)
{
	// List of codecs
	PStringList codecsToRemove;
	for (PINDEX i = 0; i < table.GetSize(); i++) {
	    H323Capability & capability = table[i];
		if (capabilityType == H323Capability::e_Video) {
		    if ((capability.GetMainType() == H323Capability::e_Video) &&
			    (capability.GetSubType() != H245_VideoCapability::e_extendedVideoCapability))
			      codecsToRemove.AppendString(capability.GetFormatName()); 
		} else if ((capabilityType == H323Capability::e_ExtendVideo) &&
             (capability.GetMainType() == H323Capability::e_Video) &&
			 (capability.GetSubType() == H245_VideoCapability::e_extendedVideoCapability)) {
                  codecsToRemove.AppendString(capability.GetFormatName());
		} else 
			 if (capability.GetMainType() == capabilityType)
			   codecsToRemove.AppendString(capability.GetFormatName());  
	}

	for (PINDEX i=0; i < codecsToRemove.GetSize(); i++) 
		      Remove(codecsToRemove[i]);

	return TRUE;
}

#ifdef H323_VIDEO
BOOL H323Capabilities::SetVideoFrameSize(H323Capability::CapabilityFrameSize frameSize, int frameUnits) 
{ 
    // Remove the unmatching capabilities
	if (frameSize != H323Capability::cifMPI) Remove("*-CIF*");
    if (frameSize != H323Capability::qcifMPI) Remove("*-QCIF*");
	if (frameSize != H323Capability::sqcifMPI) Remove("*-SQCIF*");

	// Remove Generic size Capabilities
	PStringList genericCaps;
	if ((frameSize != H323Capability::i1080MPI) &&
	    (frameSize != H323Capability::p720MPI) &&
	    (frameSize != H323Capability::i480MPI) &&
		(frameSize != H323Capability::cif16MPI) &&
        (frameSize != H323Capability::cif4MPI)) {
       	for (PINDEX i = 0; i < table.GetSize(); i++) {
	     H323Capability & capability = table[i];
		  if ((capability.GetMainType() == H323Capability::e_Video) &&
		   (capability.GetSubType() != H245_VideoCapability::e_extendedVideoCapability)) {
			 PCaselessString str = table[i].GetFormatName();
		     PString formatName = "*-*";
		     PStringArray wildcard = formatName.Tokenise('*', FALSE);
             if (!MatchWildcard(str, wildcard))
			    genericCaps.AppendString(str);
	      }
	    }
		Remove(genericCaps);
	}

	// Instruct remaining Video Capabilities to set Frame Size to new Value
	for (PINDEX i = 0; i < table.GetSize(); i++) {
	    H323Capability & capability = table[i];
	    if (capability.GetMainType() == H323Capability::e_Video)
			     capability.SetMaxFrameSize(frameSize,frameUnits);
	}
	return TRUE; 
}
#endif

void H323Capabilities::BuildPDU(const H323Connection & connection,
                                H245_TerminalCapabilitySet & pdu) const
{
  PINDEX tableSize = table.GetSize();
  PINDEX setSize = set.GetSize();
  //PAssert((tableSize > 0) == (setSize > 0), PLogicError);
  if (tableSize == 0 || setSize == 0)
    return;

  // Set the table of capabilities
  pdu.IncludeOptionalField(H245_TerminalCapabilitySet::e_capabilityTable);

  H245_H2250Capability & h225_0 = pdu.m_multiplexCapability;
  PINDEX rtpPacketizationCount = 0;

  PINDEX count = 0;
  for (PINDEX i = 0; i < tableSize; i++) {
    H323Capability & capability = table[i];
    if (capability.IsUsable(connection)) {
      pdu.m_capabilityTable.SetSize(count+1);
      H245_CapabilityTableEntry & entry = pdu.m_capabilityTable[count++];
      entry.m_capabilityTableEntryNumber = capability.GetCapabilityNumber();
      entry.IncludeOptionalField(H245_CapabilityTableEntry::e_capability);
      capability.OnSendingPDU(entry.m_capability);

      h225_0.m_mediaPacketizationCapability.m_rtpPayloadType.SetSize(rtpPacketizationCount+1);
      if (H323SetRTPPacketization(h225_0.m_mediaPacketizationCapability.m_rtpPayloadType[rtpPacketizationCount],
                                                    capability.GetMediaFormat(), RTP_DataFrame::MaxPayloadType)) {
        // Check if already in list
        PINDEX test;
        for (test = 0; test < rtpPacketizationCount; test++) {
          if (h225_0.m_mediaPacketizationCapability.m_rtpPayloadType[test] == h225_0.m_mediaPacketizationCapability.m_rtpPayloadType[rtpPacketizationCount])
            break;
        }
        if (test == rtpPacketizationCount)
          rtpPacketizationCount++;
      }
    }
  }

  // Have some mediaPacketizations to include.
  if (rtpPacketizationCount > 0) {
    h225_0.m_mediaPacketizationCapability.m_rtpPayloadType.SetSize(rtpPacketizationCount);
    h225_0.m_mediaPacketizationCapability.IncludeOptionalField(H245_MediaPacketizationCapability::e_rtpPayloadType);
  }

  // Set the sets of compatible capabilities
  pdu.IncludeOptionalField(H245_TerminalCapabilitySet::e_capabilityDescriptors);

  pdu.m_capabilityDescriptors.SetSize(setSize);
  for (PINDEX outer = 0; outer < setSize; outer++) {
    H245_CapabilityDescriptor & desc = pdu.m_capabilityDescriptors[outer];
    desc.m_capabilityDescriptorNumber = (unsigned)(outer + 1);
    desc.IncludeOptionalField(H245_CapabilityDescriptor::e_simultaneousCapabilities);
    PINDEX middleSize = set[outer].GetSize();
    desc.m_simultaneousCapabilities.SetSize(middleSize);
    for (PINDEX middle = 0; middle < middleSize; middle++) {
      H245_AlternativeCapabilitySet & alt = desc.m_simultaneousCapabilities[middle];
      PINDEX innerSize = set[outer][middle].GetSize();
      alt.SetSize(innerSize);
      count = 0;
      for (PINDEX inner = 0; inner < innerSize; inner++) {
        H323Capability & capability = set[outer][middle][inner];
        if (capability.IsUsable(connection)) {
          alt.SetSize(count+1);
          alt[count++] = capability.GetCapabilityNumber();
        }
      }
    }
  }
}


BOOL H323Capabilities::Merge(const H323Capabilities & newCaps)
{
  PTRACE(3, "H245\tCapability merge of:\n" << newCaps );
  PTRACE_IF(3, !table.IsEmpty(),  "H245\tInto:\n" << *this);

  // Add any new capabilities not already in set.
  PINDEX i;
  for (i = 0; i < newCaps.GetSize(); i++) {
    if (FindCapability(newCaps[i]) == NULL) 
      Copy(newCaps[i]);
  }

  // This should merge instead of just adding to it.
  PINDEX outerSize = newCaps.set.GetSize();
  PINDEX outerBase = set.GetSize();
  set.SetSize(outerBase+outerSize);
  for (PINDEX outer = 0; outer < outerSize; outer++) {
    PINDEX middleSize = newCaps.set[outer].GetSize();
    set[outerBase+outer].SetSize(middleSize);
    for (PINDEX middle = 0; middle < middleSize; middle++) {
      PINDEX innerSize = newCaps.set[outer][middle].GetSize();
      for (PINDEX inner = 0; inner < innerSize; inner++) {
        H323Capability * cap = FindCapability(newCaps.set[outer][middle][inner].GetCapabilityNumber());
        if (cap != NULL) 
          set[outerBase+outer][middle].Append(cap);
      }
    }
  }

  PTRACE_IF(3, !table.IsEmpty(), "H245\tCapability merge result:\n" << *this);
  PTRACE(3, "H245\tReceived capability set, is "
                 << (table.IsEmpty() ? "rejected" : "accepted"));
  return !table.IsEmpty();
}


void H323Capabilities::Reorder(const PStringArray & preferenceOrder)
{
  if (preferenceOrder.IsEmpty())
    return;

  table.DisallowDeleteObjects();

  PINDEX preference = 0;
  PINDEX base = 0;

  for (preference = 0; preference < preferenceOrder.GetSize(); preference++) {
    PStringArray wildcard = preferenceOrder[preference].Tokenise('*', FALSE);
    for (PINDEX idx = base; idx < table.GetSize(); idx++) {
      PCaselessString str = table[idx].GetFormatName();
      if (MatchWildcard(str, wildcard)) {
        if (idx != base)
          table.InsertAt(base, table.RemoveAt(idx));
        base++;
      }
    }
  }

  for (PINDEX outer = 0; outer < set.GetSize(); outer++) {
    for (PINDEX middle = 0; middle < set[outer].GetSize(); middle++) {
      H323CapabilitiesList & list = set[outer][middle];
      for (PINDEX idx = 0; idx < table.GetSize(); idx++) {
        for (PINDEX inner = 0; inner < list.GetSize(); inner++) {
          if (&table[idx] == &list[inner]) {
            list.Append(list.RemoveAt(inner));
            break;
          }
        }
      }
    }
  }

  table.AllowDeleteObjects();
}


BOOL H323Capabilities::IsAllowed(const H323Capability & capability)
{
  return IsAllowed(capability.GetCapabilityNumber());
}


BOOL H323Capabilities::IsAllowed(const unsigned a_capno)
{
  // Check that capno is actually in the set
  PINDEX outerSize = set.GetSize();
  for (PINDEX outer = 0; outer < outerSize; outer++) {
    PINDEX middleSize = set[outer].GetSize();
    for (PINDEX middle = 0; middle < middleSize; middle++) {
      PINDEX innerSize = set[outer][middle].GetSize();
      for (PINDEX inner = 0; inner < innerSize; inner++) {
        if (a_capno == set[outer][middle][inner].GetCapabilityNumber()) {
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}


BOOL H323Capabilities::IsAllowed(const H323Capability & capability1,
                                 const H323Capability & capability2)
{
  return IsAllowed(capability1.GetCapabilityNumber(),
                   capability2.GetCapabilityNumber());
}


BOOL H323Capabilities::IsAllowed(const unsigned a_capno1, const unsigned a_capno2)
{
  if (a_capno1 == a_capno2) {
    PTRACE(1, "H323\tH323Capabilities::IsAllowed() capabilities are the same.");
    return TRUE;
  }

  PINDEX outerSize = set.GetSize();
  for (PINDEX outer = 0; outer < outerSize; outer++) {
    PINDEX middleSize = set[outer].GetSize();
    for (PINDEX middle = 0; middle < middleSize; middle++) {
      PINDEX innerSize = set[outer][middle].GetSize();
      for (PINDEX inner = 0; inner < innerSize; inner++) {
        if (a_capno1 == set[outer][middle][inner].GetCapabilityNumber()) {
          /* Now go searching for the other half... */
          for (PINDEX middle2 = 0; middle2 < middleSize; ++middle2) {
            if (middle != middle2) {
              PINDEX innerSize2 = set[outer][middle2].GetSize();
              for (PINDEX inner2 = 0; inner2 < innerSize2; ++inner2) {
                if (a_capno2 == set[outer][middle2][inner2].GetCapabilityNumber()) {
                  return TRUE;
                }
              }
            }
          }
        }
      }
    }
  }
  return FALSE;
}


/////////////////////////////////////////////////////////////////////////////

#ifndef PASN_NOPRINTON


struct msNonStandardCodecDef {
  const char * name;
  BYTE sig[2];
};


static msNonStandardCodecDef msNonStandardCodec[] = {
  { "L&H CELP 4.8k", { 0x01, 0x11 } },
  { "ADPCM",         { 0x02, 0x00 } },
  { "L&H CELP 8k",   { 0x02, 0x11 } },
  { "L&H CELP 12k",  { 0x03, 0x11 } },
  { "L&H CELP 16k",  { 0x04, 0x11 } },
  { "IMA-ADPCM",     { 0x11, 0x00 } },
  { "GSM",           { 0x31, 0x00 } },
  { NULL,            { 0,    0    } }
};

void H245_AudioCapability::PrintOn(ostream & strm) const
{
  strm << GetTagName();

  // tag 0 is nonstandard
  if (tag == 0) {

    H245_NonStandardParameter & param = (H245_NonStandardParameter &)GetObject();
    const PBYTEArray & data = param.m_data;

    switch (param.m_nonStandardIdentifier.GetTag()) {
      case H245_NonStandardIdentifier::e_h221NonStandard:
        {
          H245_NonStandardIdentifier_h221NonStandard & h221 = param.m_nonStandardIdentifier;

          // Microsoft is 181/0/21324
          if ((h221.m_t35CountryCode   == 181) &&
              (h221.m_t35Extension     == 0) &&
              (h221.m_manufacturerCode == 21324)
            ) {
            PString name = "Unknown";
            PINDEX i;
            if (data.GetSize() >= 21) {
              for (i = 0; msNonStandardCodec[i].name != NULL; i++) {
                if ((data[20] == msNonStandardCodec[i].sig[0]) && 
                    (data[21] == msNonStandardCodec[i].sig[1])) {
                  name = msNonStandardCodec[i].name;
                  break;
                }
              }
            }
            strm << (PString(" [Microsoft") & name) << "]";
          }

          // Equivalence is 9/0/61
          else if ((h221.m_t35CountryCode   == 9) &&
                   (h221.m_t35Extension     == 0) &&
                   (h221.m_manufacturerCode == 61)
                  ) {
            PString name;
            if (data.GetSize() > 0)
              name = PString((const char *)(const BYTE *)data, data.GetSize());
            strm << " [Equivalence " << name << "]";
          }

          // Xiph is 181/0/38
          else if ((h221.m_t35CountryCode   == 181) &&
                   (h221.m_t35Extension     == 0) &&
                   (h221.m_manufacturerCode == 38)
                  ) {
            PString name;
            if (data.GetSize() > 0)
              name = PString((const char *)(const BYTE *)data, data.GetSize());
            strm << " [Xiph " << name << "]";
          }

          // Cisco is 181/0/18
          else if ((h221.m_t35CountryCode   == 181) &&
                   (h221.m_t35Extension     == 0) &&
                   (h221.m_manufacturerCode == 18)
                  ) {
            PString name;
            if (data.GetSize() > 0)
              name = PString((const char *)(const BYTE *)data, data.GetSize());
            strm << " [Cisco " << name << "]";
          }

        }
        break;
      default:
        break;
    }
  }

  if (choice == NULL)
    strm << " (NULL)";
  else {
    strm << ' ' << *choice;
  }

  //PASN_Choice::PrintOn(strm);
}
#endif
