<commit_before>//
// Copyright (C) 2004-2006 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2004-2006 Pingtel Corp.  All rights reserved.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
///////////////////////////////////////////////////////////////////////////////


// SYSTEM INCLUDES
#include <stdio.h>
#include <stdlib.h>

// APPLICATION INCLUDES
#include <os/OsLock.h>

#include <utl/UtlSListIterator.h>
#include <utl/UtlTokenizer.h>
#include <sdp/SdpCodec.h>
#include <net/SdpBody.h>
#include <net/NameValuePair.h>
#include <net/NameValueTokenizer.h>
#include <net/SdpCodecFactory.h>
#include <utl/UtlTokenizer.h>
#include <net/NetBase64Codec.h>

#define MAXIMUM_LONG_INT_CHARS 20
#define MAXIMUM_MEDIA_TYPES 30
#define MAXIMUM_VIDEO_SIZES 6

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
#define SDP_NAME_VALUE_DELIMITOR '='
#define SDP_SUBFIELD_SEPARATOR ' '
#define SDP_SUBFIELD_SEPARATORS "\t "

#define SDP_RTP_MEDIA_TRANSPORT_TYPE "RTP/AVP"
#define SDP_RTP_TCP_MEDIA_TRANSPORT_TYPE "TCP/RTP/AVP"
#define SDP_SRTP_MEDIA_TRANSPORT_TYPE "RTP/SAVP"
#define SDP_MLAW_PAYLOAD 0
#define SDP_ALAW_PAYLOAD 8

#define SDP_NETWORK_TYPE "IN"
#define SDP_IP4_ADDRESS_TYPE "IP4"
#define NTP_TO_EPOCH_DELTA 2208988800UL

// STATIC VARIABLE INITIALIZATIONS
static int     sSessionCount = 5 ;  // Session version for SDP body
static OsMutex sSessionLock(OsMutex::Q_FIFO) ;


/* //////////////////////////// PUBLIC //////////////////////////////////// */

/* ============================ CREATORS ================================== */

// Constructor
SdpBody::SdpBody(const char* bodyBytes, int byteCount)
 : HttpBody(bodyBytes, byteCount)
{
   mClassType = SDP_BODY_CLASS;
   remove(0);
   append(SDP_CONTENT_TYPE);

   sdpFields = new UtlSList();

   if(bodyBytes)
   {
      if(byteCount < 0)
      {
         bodyLength = strlen(bodyBytes);
      }
      parseBody(bodyBytes, byteCount);
   }
   else
   {
      // this is the mandated order of the header fields
      addValue("v", "0" );
      addValue("o", "sipX 5 5 IN IP4 127.0.0.1");
      addValue("s");
      addValue("i");
      addValue("u");
      addValue("e");
      addValue("p");
      addValue("c");
      addValue("b");
   }
}

// Copy constructor
SdpBody::SdpBody(const SdpBody& rSdpBody) :
   HttpBody(rSdpBody)
{
   mClassType = SDP_BODY_CLASS;
   if(rSdpBody.sdpFields)
   {
      sdpFields = new UtlSList();

      NameValuePair* headerField;
      NameValuePair* copiedHeader = NULL;
      UtlSListIterator iterator((UtlSList&)(*(rSdpBody.sdpFields)));
      while((headerField = (NameValuePair*) iterator()))
      {
         copiedHeader = new NameValuePair(*headerField);
         sdpFields->append(copiedHeader);
      }
   }
   else
   {
      sdpFields = NULL;
   }
}

// Destructor
SdpBody::~SdpBody()
{
   if(sdpFields)
   {
      while(! sdpFields->isEmpty())
      {
         delete sdpFields->get();
      }
      delete sdpFields;
   }
}

/* ============================ MANIPULATORS ============================== */

void SdpBody::parseBody(const char* bodyBytes, int byteCount)
{
   if(byteCount < 0)
   {
      bodyLength = strlen(bodyBytes);
   }

   if(bodyBytes)
   {
      UtlString name;
      UtlString value;
      int nameFound;
      NameValuePair* nameValue;

      NameValueTokenizer parser(bodyBytes, byteCount);
      do
      {
         name.remove(0);
         value.remove(0);
         nameFound = parser.getNextPair(SDP_NAME_VALUE_DELIMITOR,
                                        &name, &value);
         if(nameFound)
         {
            nameValue = new NameValuePair(name.data(), value.data());
            sdpFields->append(nameValue);

         }
      }
      while(nameFound);
   }
}


// Assignment operator
SdpBody&
SdpBody::operator=(const SdpBody& rhs)
{
   if (this == &rhs)            // handle the assignment to self case
      return *this;

   // Copy the base class stuff
   this->HttpBody::operator=((const HttpBody&)rhs);

   if(sdpFields)
   {
      sdpFields->destroyAll();
   }

   if(rhs.sdpFields)
   {
      if(sdpFields == NULL)
      {
         sdpFields = new UtlSList();
      }

      NameValuePair* headerField;
      NameValuePair* copiedHeader = NULL;
      UtlSListIterator iterator((UtlSList&)(*(rhs.sdpFields)));
      while((headerField = (NameValuePair*) iterator()))
      {
         copiedHeader = new NameValuePair(*headerField);
         sdpFields->append(copiedHeader);
      }
   }

   // Set the class type just to play it safe
   mClassType = SDP_BODY_CLASS;
   return *this;
}

/* ============================ ACCESSORS ================================= */
void SdpBody::setStandardHeaderFields(const char* sessionName,
                                      const char* emailAddress,
                                      const char* phoneNumber,
                                      const char* originatorAddress)
{
   OsLock lock (sSessionLock) ;

   setOriginator("sipX", 5, sSessionCount++,
                 (originatorAddress && *originatorAddress) ?
                 originatorAddress : "127.0.0.1");
   setSessionNameField(sessionName);
   if(emailAddress && strlen(emailAddress) > 0)
   {
      setEmailAddressField(emailAddress);
   }
   if(phoneNumber && strlen(phoneNumber) > 0)
   {
      setPhoneNumberField(phoneNumber);
   }
}

void SdpBody::setSessionNameField(const char* sessionName)
{
   setValue("s", sessionName);
}

void SdpBody::setEmailAddressField(const char* emailAddress)
{
   setValue("e", emailAddress);
}

void SdpBody::setPhoneNumberField(const char* phoneNumber)
{
   setValue("p", phoneNumber);
}

void SdpBody::setValue(const char* name, const char* value)
{
   NameValuePair nvToMatch(name);
   NameValuePair* nvFound = NULL;
   UtlSListIterator iterator(*sdpFields);

   nvFound = (NameValuePair*) iterator.findNext(&nvToMatch);
   if(nvFound)
   {
      // field exists - replace the value
      nvFound->setValue(value);
   }
   else
   {
      addValue(name, value);
   }
}

int SdpBody::getMediaSetCount() const
{
   UtlSListIterator iterator(*sdpFields);
   NameValuePair mediaName("m");
   int count = 0;
   while(iterator.findNext(&mediaName))
   {
      count++;
   }
   return(count);

}

UtlBoolean SdpBody::getMediaType(int mediaIndex, UtlString* mediaType) const
{
   return(getMediaSubfield(mediaIndex, 0, mediaType));
}

UtlBoolean SdpBody::getMediaPort(int mediaIndex, int* port) const
{
   UtlString portString;
   UtlBoolean portFound = getMediaSubfield(mediaIndex, 1, &portString);
   int portCountSeparator;

   if(!portString.isNull())
   {
      // Remove the port pair count if it exists
      portCountSeparator = portString.index("/");
      if(portCountSeparator >= 0)
      {
         portString.remove(portCountSeparator);
      }

      *port = atoi(portString.data());
      portFound = TRUE;
   }

   return(portFound);
}

UtlBoolean SdpBody::getMediaRtcpPort(int mediaIndex, int* port) const
{
    UtlBoolean bFound = FALSE ;
    int iRtpPort ;
    
    if (getMediaPort(mediaIndex, &iRtpPort))
    {
        bFound = TRUE ;
        *port = iRtpPort + 1;

        UtlSListIterator iterator(*sdpFields);
        NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
        if(nv)
        {
            while ((nv = findFieldNameBefore(&iterator, "a", "m")))
            {
                //printf("->%s:%s\n", nv->data(), nv->getValue()) ;

                UtlString typeAttribute ;
                UtlString portAttribute ;

                NameValueTokenizer::getSubField(nv->getValue(), 0, 
                        ":", &typeAttribute) ;
                NameValueTokenizer::getSubField(nv->getValue(), 1,
                        ":", &portAttribute) ;

                if (typeAttribute.compareTo("rtcp", UtlString::ignoreCase) == 0)
                {
                    *port = atoi(portAttribute.data()) ;
                    
                }
            }
        }
    }

    return bFound ;
}

UtlBoolean SdpBody::getMediaProtocol(int mediaIndex, UtlString* transportProtocol) const
{
   return(getMediaSubfield(mediaIndex, 2, transportProtocol));
}

UtlBoolean SdpBody::getMediaPayloadType(int mediaIndex, int maxTypes,
                                        int* numTypes, int payloadTypes[]) const
{
    UtlString payloadTypeString;
    int typeCount = 0;
    int index = 0 ;

    while (index < maxTypes &&
            getMediaSubfield(mediaIndex, 3 + index++, &payloadTypeString))
    {
        if(!payloadTypeString.isNull())
        {
            // Add the payload type and increment typeCount if not 
            // already in the list
            bool bFound = false ;
            int payload = atoi(payloadTypeString.data()) ;
            for (int i=0; i<typeCount; i++)
            {
                if (payloadTypes[i] == payload)
                {
                    bFound = true ;
                    break ;
                }
            }

            if (!bFound)
            {            
                payloadTypes[typeCount] = payload ;
                typeCount++;
            }
        }
    }

    *numTypes = typeCount;

    return(typeCount > 0);
}

UtlBoolean SdpBody::getMediaSubfield(int mediaIndex, int subfieldIndex, UtlString* subField) const
{
   UtlBoolean subfieldFound = FALSE;
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
   const char* value;
   subField->remove(0);

   if(nv)
   {
      value =  nv->getValue();
      NameValueTokenizer::getSubField(value, subfieldIndex,
                                      SDP_SUBFIELD_SEPARATORS, subField);
      if(!subField->isNull())
      {
         subfieldFound = TRUE;
      }
   }
   return(subfieldFound);
}

UtlBoolean SdpBody::getPayloadRtpMap(int payloadType,
                                     UtlString& mimeSubtype,
                                     int& sampleRate,
                                     int& numChannels) const
{
   // an "a" record look something like:
   // "a=rtpmap:<payloadType> <mimeSubtype/sampleRate[/numChannels]"

   // Loop through all of the "a" records
   UtlBoolean foundRtpMap = FALSE;
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = NULL;
   int aFieldIndex = 0;
   const char* value;
   UtlString aFieldType;
   UtlString payloadString;
   UtlString sampleRateString;
   UtlString numChannelString;
   UtlString aFieldMatch("a");

   while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
   {
      value =  nv->getValue();

      // Verify this is an rtpmap "a" record
      NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                      &aFieldType);
      if(aFieldType.compareTo("rtpmap") == 0)
      {
         // If this is the rtpmap for the requested payload type
         NameValueTokenizer::getSubField(value, 1,
                                         " \t:/", // seperators
                                         &payloadString);
         if(atoi(payloadString.data()) == payloadType)
         {
            // The mime subtype is the 3nd subfield
            NameValueTokenizer::getSubField(value, 2,
                                            " \t:/", // seperators
                                            &mimeSubtype);

            // The sample rate is the 4rd subfield
            NameValueTokenizer::getSubField(value, 3,
                                            " \t:/", // seperators
                                            &sampleRateString);
            sampleRate = atoi(sampleRateString.data());
            if(sampleRate <= 0) sampleRate = -1;

            // The number of channels is the 5th subfield
            NameValueTokenizer::getSubField(value, 4,
                                            " \t:/", // seperators
                                            &numChannelString);
            numChannels = atoi(numChannelString.data());
            if(numChannels <= 0) numChannels = -1;

            // we are all done
            foundRtpMap = TRUE;
            break;
         }
      }

      aFieldIndex++;
   }
   return(foundRtpMap);
}

UtlBoolean SdpBody::getPayloadFormat(int payloadType,
                                     UtlString& fmtp,
                                     int& valueFmtp,
                                     int& numVideoSizes,
                                     int videoSizes[]) const
{
   // an "a" record look something like:
   // "a=fmtp:<payloadType> <fmtpdata>"

   // Loop through all of the "a" records
   UtlBoolean foundPayloadFmtp = FALSE;
   UtlBoolean foundField;
   UtlSListIterator iterator(*sdpFields);
   UtlString aFieldType;
   UtlString payloadString;
   UtlString modifierString;
   NameValuePair* nv = NULL;
   int index = 0;
   int aFieldIndex = 0;
   int videoIndex = 0;
   const char* value;
   UtlString temp;
   UtlString aFieldMatch("a");

   numVideoSizes = 0;
   valueFmtp = 0;

   while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
   {
      value =  nv->getValue();

      // Verify this is an fmtp "a" record
      NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                      &aFieldType);
      if(aFieldType.compareTo("fmtp") == 0)
      {
         NameValueTokenizer::getSubField(value, 1,
                                         " \t:/", // seperators
                                         &payloadString);
         if(atoi(payloadString.data()) == payloadType)
         {
            const char *fmtpSubField;
            int subFieldLen;
            // If this is the fmtp for the requested payload type
            foundField = NameValueTokenizer::getSubField(value, -1, 2, 
                                                         " \t:",  // seperators
                                                         fmtpSubField,
                                                         subFieldLen,
                                                         0);
            if(foundField) 
            {
               fmtp = fmtpSubField;
            }

            foundPayloadFmtp = TRUE;
            // Get modifier
            NameValueTokenizer::getSubField(value, 2,
                                                    " \t:=", // seperators
                                                    &modifierString);
            // Get value
            foundField = NameValueTokenizer::getSubField(value, 3,
                                                    " \t:=", // seperators
                                                    &temp);


            valueFmtp = 0;
            index = 3;
            if (modifierString.compareTo("mode") == 0)
            {
                valueFmtp  = atoi(temp.data());
            }
            else if (modifierString.compareTo("imagesize") == 0)
            {
                // Checking size information for imagesize modifier
                valueFmtp = atoi(temp.data());
                switch (valueFmtp)
                {
                case 0: 
                    videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QCIF;
                    break;
                case 1:
                    videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_CIF;
                    break;
                }
                valueFmtp = 0;
                numVideoSizes = videoIndex;
            }
            else if (modifierString.compareTo("size") == 0)
            {
                // Checking size information for other modifier
                while (foundField && index < 8 && videoIndex < MAXIMUM_VIDEO_SIZES)
                {
                    foundField = NameValueTokenizer::getSubField(value, index++,
                                                                " \t/:", // seperators
                                                                &temp);
                    if (temp.compareTo("CIF") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_CIF;
                    }
                    else if (temp.compareTo("QVGA") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QVGA;
                    }
                    else if (temp.compareTo("QCIF") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QCIF;
                    }
                    else if (temp.compareTo("SQCIF") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_SQCIF;
                    }
                }
                numVideoSizes = videoIndex;
             }
             if (videoIndex == 0)
             {
                 // Default to QCIF if no size is present
                 videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QCIF;
                 numVideoSizes = 1;
             }
         }
      }

      aFieldIndex++;
   }
   return(foundPayloadFmtp);
}

UtlBoolean SdpBody::getSrtpCryptoField(int mediaIndex,
                                       int index,
                                       SdpSrtpParameters& params) const
{
    UtlBoolean foundCrypto = FALSE;
    UtlBoolean foundField;
    UtlString aFieldType;
    UtlSListIterator iterator(*sdpFields);
    NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
    const char* value;
    UtlString indexString;
    UtlString cryptoSuite;
    UtlString temp;
    int size;
    char srtpKey[SRTP_KEY_LENGTH+1];

    size = sdpFields->entries();
    while ((nv = findFieldNameBefore(&iterator, "a", "m")))
    {
        value =  nv->getValue();

        // Verify this is an crypto "a" record
        NameValueTokenizer::getSubField(value, 0,
                                        " \t:/", // seperators
                                        &aFieldType);
        if(aFieldType.compareTo("crypto") == 0)
        {
            NameValueTokenizer::getSubField(value, 1,
                                            " \t:/", // seperators
                                            &indexString);
            if(atoi(indexString.data()) == index)
            {
                foundCrypto = TRUE;

                // Encryption & authentication on by default
                params.securityLevel = SRTP_ENCRYPTION | SRTP_AUTHENTICATION;

                NameValueTokenizer::getSubField(value, 2,
                                                " \t:/", // seperators
                                                &cryptoSuite);
                // Check the crypto suite 
                if (cryptoSuite.compareTo("AES_CM_128_HMAC_SHA1_80") == 0)
                {
                    params.cipherType = AES_CM_128_HMAC_SHA1_80;
                }
                else if (cryptoSuite.compareTo("AES_CM_128_HMAC_SHA1_32") == 0)
                {
                    params.cipherType = AES_CM_128_HMAC_SHA1_32;
                }
                else if (cryptoSuite.compareTo("F8_128_HMAC_SHA1_80") == 0)
                {
                    params.cipherType = F8_128_HMAC_SHA1_80;
                }
                else
                {
                    //Couldn't find crypto suite, no secritiy
                    params.securityLevel = 0;
                }

                // Get key
                foundField = NameValueTokenizer::getSubField(value, 4,
                                                             " \t/:|", // seperators
                                                             &temp);
                NetBase64Codec::decode(temp.length(), temp.data(), size, srtpKey);
                if (size <= SRTP_KEY_LENGTH)
                {
                    srtpKey[size] = 0;
                }
                else
                {
                    srtpKey[SRTP_KEY_LENGTH] = 0;
                }
                strcpy((char*)params.masterKey, srtpKey);

                // Modify security level with session parameters
                for (int index=5; foundField; ++index)
                {
                    foundField = NameValueTokenizer::getSubField(value, index,
                                                                 " \t/:|", // seperators
                                                                 &temp);
                    if (foundField)
                    {
                        if (temp.compareTo("UNENCRYPTED_SRTP") == 0)
                        {
                            params.securityLevel &= ~SRTP_ENCRYPTION;
                        }
                        if (temp.compareTo("UNAUTHENTICATED_SRTP") == 0)
                        {
                            params.securityLevel &= ~SRTP_AUTHENTICATION;
                        }
                    }
                }
                break;
            }
        }
    }
    return foundCrypto;
}

UtlBoolean SdpBody::getFramerateField(int mediaIndex,
                                      int& videoFramerate) const
{
    UtlBoolean foundFramerate = FALSE;
    UtlString aFieldType;
    UtlSListIterator iterator(*sdpFields);
    NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
    const char* value;
    UtlString aFieldMatch("a");
    UtlString rateString;
    int size;
    UtlString temp;
    videoFramerate = 0;

    size = sdpFields->entries();
    while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
    {
        value =  nv->getValue();

        // Verify this is an crypto "a" record
        NameValueTokenizer::getSubField(value, 0,
                                        " \t:/", // seperators
                                        &aFieldType);
        if(aFieldType.compareTo("framerate") == 0)
        {
            NameValueTokenizer::getSubField(value, 1,
                                            " \t:/", // seperators
                                            &rateString);
            videoFramerate = atoi(rateString.data());
            foundFramerate = TRUE;
        }
    }
    return foundFramerate;
}

UtlBoolean SdpBody::getBandwidthField(int& bandwidth) const
{
    UtlBoolean bFound = FALSE;

   // Loop through all of the "b" records
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = NULL;
   const char* value;
   UtlString aFieldMatch("b");
   UtlString aFieldModifier;
   UtlString temp;

   // Indicate no "b" field was sent with 0
   bandwidth  = 0;

   while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
   {
      value =  nv->getValue();

      // Verify this is an crypto "a" record
      NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                        &aFieldModifier);
      if(aFieldModifier.compareTo("CT") == 0)
      {
            NameValueTokenizer::getSubField(value, 1,
                                            " \t:/", // seperators
                                            &temp);

            bandwidth = atoi(temp.data());
            bFound = TRUE;
      }
   }
   return bFound;
}

UtlBoolean SdpBody::getValue(int fieldIndex, UtlString* name, UtlString* value) const
{
   NameValuePair* nv = NULL;
   name->remove(0);
   value->remove(0);
   if(fieldIndex >=0)
   {
      nv = (NameValuePair*) sdpFields->at(fieldIndex);
      if(nv)
      {
         *name = *nv;
         *value = nv->getValue();
      }
   }
   return(nv != NULL);
}

UtlBoolean SdpBody::getMediaData(int mediaIndex, UtlString* mediaType,
                                 int* mediaPort, int* mediaPortPairs,
                                 UtlString* mediaTransportType,
                                 int maxPayloadTypes, int* numPayloadTypes,
                                 int payloadTypes[]) const
{
   UtlBoolean fieldFound = FALSE;
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
   const char* value;
   UtlString portString;
   UtlString portPairString;
   int portCountSeparator;
   int typeCount = 0;
   UtlString payloadTypeString;

   if(nv)
   {
      fieldFound = TRUE;
      value =  nv->getValue();

      // media Type
      NameValueTokenizer::getSubField(value, 0,
                                      SDP_SUBFIELD_SEPARATORS, mediaType);

      // media port and media port pair count
      NameValueTokenizer::getSubField(value, 1,
                                      SDP_SUBFIELD_SEPARATORS, &portString);
      if(!portString.isNull())
      {
         // Copy for obtaining the pair count
         portPairString.append(portString);

         // Remove the port pair count if it exists
         portCountSeparator = portString.index("/");
         if(portCountSeparator >= 0)
         {
            portString.remove(portCountSeparator);

            // Get the other part of the field
            portPairString.remove(0, portCountSeparator + 1);
         }
         else
         {
            portPairString.remove(0);
         }

         *mediaPort = atoi(portString.data());
         if(portPairString.isNull())
         {
            *mediaPortPairs = 1;
         }
         else
         {
            *mediaPortPairs = atoi(portPairString.data());
         }
      }
      else
      {
         *mediaPort = 0;
         *mediaPortPairs = 0;
      }

      // media transport type
      NameValueTokenizer::getSubField(value, 2,
                                      SDP_SUBFIELD_SEPARATORS, mediaTransportType);

      // media payload/codec types
      NameValueTokenizer::getSubField(value,  3 + typeCount,
                                      SDP_SUBFIELD_SEPARATORS, &payloadTypeString);
      while(typeCount < maxPayloadTypes &&
            !payloadTypeString.isNull())
      {
         payloadTypes[typeCount] = atoi(payloadTypeString.data());
         typeCount++;
         NameValueTokenizer::getSubField(value,  3 + typeCount,
                                         SDP_SUBFIELD_SEPARATORS, &payloadTypeString);
      }
      *numPayloadTypes = typeCount;
   }

   return(fieldFound);
}

int SdpBody::findMediaType(const char* mediaType, int startMediaIndex) const
{
   NameValuePair* mediaField;
   UtlSListIterator iterator(*sdpFields);
   UtlBoolean mediaTypeFound = FALSE;
   int index = startMediaIndex;
   NameValuePair mediaName("m");
   mediaField = positionFieldInstance(startMediaIndex, &iterator, "m");
   const char* value;

   while(mediaField && ! mediaTypeFound)
   {
      value = mediaField->getValue();
      if(strstr(value, mediaType) == value)
      {
         mediaTypeFound = TRUE;
         break;
      }

      mediaField = (NameValuePair*) iterator.findNext(&mediaName);
      index++;
   }

   if(! mediaTypeFound)
   {
      index = -1;
   }
   return(index);
}

UtlBoolean SdpBody::getMediaAddress(int mediaIndex, UtlString* address) const
{
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv;
   address->remove(0);
   const char* value = NULL;
   int ttlIndex;

   // Try to find a specific address for the given media set
   nv = positionFieldInstance(mediaIndex, &iterator, "m");
   if(nv)
   {
      nv = findFieldNameBefore(&iterator, "c", "m");
      if(nv)
      {
         value = nv->getValue();
         if(value)
         {
            NameValueTokenizer::getSubField(value, 2,
                                            SDP_SUBFIELD_SEPARATORS, address);
         }
      }

      // Did not find a specific address try to find the default
      if(address->isNull())
      {
         iterator.reset();
         nv = findFieldNameBefore(&iterator, "c", "m");

         // Default address exists in the header
         if(nv)
         {
            value = nv->getValue();
            if(value)
            {
               NameValueTokenizer::getSubField(value, 2,
                                               SDP_SUBFIELD_SEPARATORS, address);
            }
         }
      }

      if(!address->isNull())
      {
         // Check if there is a time to live attribute and remove it
         ttlIndex = address->index("/");
         if(ttlIndex >= 0)
         {
            address->remove(ttlIndex);
         }
      }
   }

   return(!address->isNull());
}

UtlBoolean SdpBody::getPtime(int mediaIndex, int& pTime) const
{
    UtlBoolean foundPtime = FALSE;
    UtlSListIterator iterator(*sdpFields);
    NameValuePair* nv;
    pTime = 0;
    const char* value = NULL;

    // Try to find a specific address for the given media set
    nv = positionFieldInstance(mediaIndex, &iterator, "m");
    if(nv)
    {
        UtlString aParameterName;
        UtlString pTimeValueString;
        while((nv = findFieldNameBefore(&iterator, "a", "m")))
        {
            value = nv->getValue();
            if(value)
            {
                // Get the a line parameter name
                NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                      &aParameterName);
                // See if this is a ptime parameter
                if(aParameterName.compareTo("ptime") == 0)
                {
                     // get the ptime value
                    NameValueTokenizer::getSubField(value, 1,
                                                    " \t:/", // seperators, 
                                                    &pTimeValueString);
                    if(!pTimeValueString.isNull())
                    {
                        pTime = atoi(pTimeValueString);
                        // should only be one ptime per media set (m line)
                        // Ignore all but the first one.
                        if(pTime > 0)
                        {
                            foundPtime = TRUE;
                            break;
                        }
                    }
                }
            }
        }
    }
    return(foundPtime);
}

void SdpBody::getBestAudioCodecs(int numRtpCodecs, SdpCodec rtpCodecs[],
                                 UtlString* rtpAddress, int* rtpPort,
                                 int* sendCodecIndex,
                                 int* receiveCodecIndex) const
{

   int mediaIndex = 0;
   UtlBoolean sendCodecFound = FALSE;
   UtlBoolean receiveCodecFound = FALSE;
   int numTypes;
   int payloadTypes[MAXIMUM_MEDIA_TYPES];
   int typeIndex;
   int codecIndex;

   rtpAddress->remove(0);
   *rtpPort = 0;
   *sendCodecIndex = -1;
   *receiveCodecIndex = -1;

   while(mediaIndex >= 0 &&
         (!sendCodecFound || !receiveCodecFound))
   {
      mediaIndex = findMediaType(SDP_AUDIO_MEDIA_TYPE, mediaIndex);

      if(mediaIndex >= 0)
      {
         getMediaPort(mediaIndex, rtpPort);

         if(*rtpPort >= 0)
         {
            getMediaPayloadType(mediaIndex, MAXIMUM_MEDIA_TYPES,
                                &numTypes, payloadTypes);
            for(typeIndex = 0; typeIndex < numTypes; typeIndex++)
            {
               // Until the real SdpCodec is needed we assume all of
               // the rtpCodecs are send AND receive.
               // We are also going to cheat and assume that all of
               // the media records are send AND receive
               for(codecIndex = 0; codecIndex < numRtpCodecs; codecIndex++)
               {
                  if(payloadTypes[typeIndex] ==
                     (rtpCodecs[codecIndex]).getCodecType())
                  {
                     sendCodecFound = TRUE;
                     receiveCodecFound = TRUE;
                     *sendCodecIndex = codecIndex;
                     *receiveCodecIndex = codecIndex;
                     getMediaAddress(mediaIndex, rtpAddress);
                     getMediaPort(mediaIndex, rtpPort);
                     break;
                  }
               }
               if(sendCodecFound && receiveCodecFound)
               {
                  break;
               }
            }
         }
         mediaIndex++;
      }
   }
}


void SdpBody::getBestAudioCodecs(SdpCodecFactory& localRtpCodecs,
                                 int& numCodecsInCommon,
                                 SdpCodec**& codecsInCommonArray,
                                 UtlString& rtpAddress, 
                                 int& rtpPort,
                                 int& rtcpPort,
                                 int& videoRtpPort,
                                 int& videoRtcpPort,
                                 SdpSrtpParameters& localSrtpParams,
                                 SdpSrtpParameters& matchingSrtpParams,
                                 int localBandwidth,
                                 int& matchingBandwidth,
                                 int localVideoFramerate,
                                 int& matchingVideoFramerate) const
{

   int mediaAudioIndex = 0;
   int mediaVideoIndex = 0;
   int numAudioTypes;
   int numVideoTypes;
   int bandwidth;
   int framerate;
   int audioPayloadTypes[MAXIMUM_MEDIA_TYPES];
   int videoPayloadTypes[MAXIMUM_MEDIA_TYPES];
   numCodecsInCommon = 0;
   codecsInCommonArray = new SdpCodec*[localRtpCodecs.getCodecCount()];
   SdpSrtpParameters remoteSrtpParams;

   memset((void*)&remoteSrtpParams, 0, sizeof(SdpSrtpParameters));
   memset((void*)&matchingSrtpParams, 0, sizeof(SdpSrtpParameters));
   rtpAddress.remove(0);
   rtpPort = 0;
   videoRtpPort = 0 ;

   while(mediaAudioIndex >= 0 || mediaVideoIndex >=0)
   {
      mediaAudioIndex = findMediaType(SDP_AUDIO_MEDIA_TYPE, mediaAudioIndex);
      mediaVideoIndex = findMediaType(SDP_VIDEO_MEDIA_TYPE, mediaVideoIndex);

      getBandwidthField(bandwidth);
      getBandwidthInCommon(localBandwidth, bandwidth, matchingBandwidth);

      getFramerateField(mediaVideoIndex, framerate);
      getVideoFramerateInCommon(localVideoFramerate, framerate, matchingVideoFramerate);

      if(mediaAudioIndex >= 0)
      {
         // This is kind of a bad assumption if there is more
         // than one media field, each might have a different
         // port and address
         getMediaPort(mediaAudioIndex, &rtpPort);
         getMediaRtcpPort(mediaAudioIndex, &rtcpPort);
         getSrtpCryptoField(mediaAudioIndex, 1, remoteSrtpParams);
         getMediaPort(mediaVideoIndex, &videoRtpPort);
         getMediaRtcpPort(mediaVideoIndex, &videoRtcpPort);
         getMediaAddress(mediaAudioIndex, &rtpAddress);

         if(rtpPort >= 0)
         {
            getMediaPayloadType(mediaAudioIndex, MAXIMUM_MEDIA_TYPES,
                                &numAudioTypes, audioPayloadTypes);

            getMediaPayloadType(mediaVideoIndex, MAXIMUM_MEDIA_TYPES,
                                &numVideoTypes, videoPayloadTypes);

            getCodecsInCommon(numAudioTypes,
                              numVideoTypes,
                              audioPayloadTypes,
                              videoPayloadTypes,
                              videoRtpPort,
                              localRtpCodecs,
                              numCodecsInCommon,
                              codecsInCommonArray);

            getEncryptionInCommon(localSrtpParams, remoteSrtpParams, matchingSrtpParams);

            if (numCodecsInCommon >0)
               break;
         }
         mediaAudioIndex++;
         mediaVideoIndex++;
      }
   }
}


void SdpBody::getEncryptionInCommon(SdpSrtpParameters& audioParams,
                                    SdpSrtpParameters& remoteParams,
                                    SdpSrtpParameters& commonAudioParams) const
{
    memset(&commonAudioParams, 0, sizeof(SdpSrtpParameters));

    // Test for same cipher type and remote encryption being on
    if (audioParams.cipherType == remoteParams.cipherType && remoteParams.securityLevel)
    {
        // If we agree on a cipherType then the caller controls
        // if encryption is on - even if we switched it off locally.
        memcpy(&commonAudioParams, &remoteParams, sizeof(SdpSrtpParameters));
        // Set encryption on sending and receiving - if we can figure out
        // how to negotiate this we can be more selective
        commonAudioParams.securityLevel |= SRTP_SEND | SRTP_RECEIVE;
    }
}


void SdpBody::getBandwidthInCommon(int localBandwidth,
                                   int remoteBandwidth,
                                   int& commonBandwidth) const
{
    if (localBandwidth != 0 && remoteBandwidth != 0)
    {
        commonBandwidth = (remoteBandwidth <= localBandwidth) ? 
                                remoteBandwidth : localBandwidth;
    }
    else if (remoteBandwidth != 0)
    { 
        commonBandwidth = remoteBandwidth;
    }
    else
    {
        commonBandwidth = localBandwidth;
    }
}

void SdpBody::getVideoFramerateInCommon(int localVideoFramerate,
                                        int remoteVideoFramerate,
                                        int& commonVideoFramerate) const
{
    if (remoteVideoFramerate != 0 && remoteVideoFramerate < localVideoFramerate)
    {
        commonVideoFramerate = remoteVideoFramerate;
    }
    else
    {
        commonVideoFramerate = localVideoFramerate;
    }
}

void SdpBody::getCodecsInCommon(int audioPayloadIdCount,
                                int videoPayloadIdCount,
                                int audioPayloadTypes[],
                                int videoPayloadTypes[],
                                int videoRtpPort,
                                SdpCodecFactory& localRtpCodecs,
                                int& numCodecsInCommon,
                                SdpCodec* codecsInCommonArray[]) const
{
   UtlString mimeSubtype;
   UtlString fmtp;
   int sampleRate;
   int numChannels;
   const SdpCodec* matchingCodec = NULL;
   int typeIndex;
   UtlBoolean commonCodec;
   int videoSizes[MAXIMUM_VIDEO_SIZES];
   int numVideoSizes;

   numCodecsInCommon = 0;
   memset((void*)videoSizes, 0, sizeof(videoSizes));

   // TODO: properly support media sets
   // This is a bit of a mess.  We have completely blurred over the
   // concept of separate m lines other than the mime type separation
   // of audio and video.  There may be different properties for the
   // same codecs which may appear in more than one media section
   // (m line)

   // So for example the ptime property is specific to a media
   // set (m line).  At this point we no longer know which codec
   // comes from which media set.  So for now we search through the
   // audio media sets until we find a ptime and assume the ptime
   // applies to all audio codecs.  ptime mostly only make sense for
   // audio
   int mediaSetIndex = 0;
   int defaultPtime = 0;
   UtlString mediaType;
   while(getMediaType(mediaSetIndex, &mediaType))
   {
       if(mediaType.compareTo(MIME_TYPE_AUDIO) == 0)
       {
           if(getPtime(mediaSetIndex, defaultPtime) &&
               defaultPtime > 0)
           {
               break;
           }
       }
       mediaSetIndex++;
   }

   for(typeIndex = 0; typeIndex < audioPayloadIdCount; typeIndex++)
   {
      // Until the real SdpCodec is needed we assume all of
      // the rtpCodecs are send AND receive.
      // We are also going to cheat and assume that all of
      // the media records are send AND receive

      // Get the rtpmap for the payload type
      if(getPayloadRtpMap(audioPayloadTypes[typeIndex],
                          mimeSubtype, sampleRate, numChannels))
      {
         
         // Find a match for the mime type
         matchingCodec = localRtpCodecs.getCodec(MIME_TYPE_AUDIO, mimeSubtype.data());
         if((matchingCodec != NULL) &&
            (matchingCodec->getSampleRate() == sampleRate ||
             sampleRate == -1) &&
            (matchingCodec->getNumChannels() == numChannels ||
             numChannels == -1))
         {
            commonCodec = TRUE;
            int frameSize = 0;

            if (matchingCodec->getCodecType() == SdpCodec::SDP_CODEC_ILBC)
            {
                getPayloadFormat(audioPayloadTypes[typeIndex], fmtp, frameSize, 
                                 numVideoSizes, videoSizes);
                if (frameSize == 20 || frameSize == 30 || frameSize == 0)
                {
                   if (frameSize == 0)
                   {
                       frameSize = 20;
                   }

                }
                else
                {
                    // Nothing in common here
                    commonCodec = FALSE;
                }
            }
            else if(defaultPtime > 0)
            {
                frameSize = defaultPtime;
            }

            // Create a copy of the SDP codec and set
            // the payload type for it
            if (commonCodec)
            {
               codecsInCommonArray[numCodecsInCommon] =
                  new SdpCodec(*matchingCodec);
               codecsInCommonArray[numCodecsInCommon]->setCodecPayloadFormat(audioPayloadTypes[typeIndex]);
               if (frameSize)
               {
                  codecsInCommonArray[numCodecsInCommon]->setPacketSize(frameSize*1000);
               }

            }
         
            numCodecsInCommon++;

         }
      }

      // If no payload type set and this is a static payload
      // type assume the payload type is the same as our internal
      // codec id
      else if(audioPayloadTypes[typeIndex] <=
              SdpCodec::SDP_CODEC_MAXIMUM_STATIC_CODEC)
      {
         if((matchingCodec = localRtpCodecs.getCodecByType(audioPayloadTypes[typeIndex])))
         {
            // Create a copy of the SDP codec and set
            // the payload type for it
            codecsInCommonArray[numCodecsInCommon] =
               new SdpCodec(*matchingCodec);
            codecsInCommonArray[numCodecsInCommon]->setCodecPayloadFormat(audioPayloadTypes[typeIndex]);

            numCodecsInCommon++;
         }
      }
   }
   if (videoRtpPort != 0)
   {
        for(typeIndex = 0; typeIndex < videoPayloadIdCount; typeIndex++)
        {
            int videoFmtp = 0;
            SdpCodec** sdpCodecArray;
            int numCodecs;
            int codecIndex;
            int videoSize;
            // Until the real SdpCodec is needed we assume all of
            // the rtpCodecs are send AND receive.
            // We are also going to cheat and assume that all of<commit_msg>Set ptime value in codec for case when the payload format is not explicitly defined.<commit_after>//
// Copyright (C) 2004-2006 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2004-2006 Pingtel Corp.  All rights reserved.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
///////////////////////////////////////////////////////////////////////////////


// SYSTEM INCLUDES
#include <stdio.h>
#include <stdlib.h>

// APPLICATION INCLUDES
#include <os/OsLock.h>

#include <utl/UtlSListIterator.h>
#include <utl/UtlTokenizer.h>
#include <sdp/SdpCodec.h>
#include <net/SdpBody.h>
#include <net/NameValuePair.h>
#include <net/NameValueTokenizer.h>
#include <net/SdpCodecFactory.h>
#include <utl/UtlTokenizer.h>
#include <net/NetBase64Codec.h>

#define MAXIMUM_LONG_INT_CHARS 20
#define MAXIMUM_MEDIA_TYPES 30
#define MAXIMUM_VIDEO_SIZES 6

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
#define SDP_NAME_VALUE_DELIMITOR '='
#define SDP_SUBFIELD_SEPARATOR ' '
#define SDP_SUBFIELD_SEPARATORS "\t "

#define SDP_RTP_MEDIA_TRANSPORT_TYPE "RTP/AVP"
#define SDP_RTP_TCP_MEDIA_TRANSPORT_TYPE "TCP/RTP/AVP"
#define SDP_SRTP_MEDIA_TRANSPORT_TYPE "RTP/SAVP"
#define SDP_MLAW_PAYLOAD 0
#define SDP_ALAW_PAYLOAD 8

#define SDP_NETWORK_TYPE "IN"
#define SDP_IP4_ADDRESS_TYPE "IP4"
#define NTP_TO_EPOCH_DELTA 2208988800UL

// STATIC VARIABLE INITIALIZATIONS
static int     sSessionCount = 5 ;  // Session version for SDP body
static OsMutex sSessionLock(OsMutex::Q_FIFO) ;


/* //////////////////////////// PUBLIC //////////////////////////////////// */

/* ============================ CREATORS ================================== */

// Constructor
SdpBody::SdpBody(const char* bodyBytes, int byteCount)
 : HttpBody(bodyBytes, byteCount)
{
   mClassType = SDP_BODY_CLASS;
   remove(0);
   append(SDP_CONTENT_TYPE);

   sdpFields = new UtlSList();

   if(bodyBytes)
   {
      if(byteCount < 0)
      {
         bodyLength = strlen(bodyBytes);
      }
      parseBody(bodyBytes, byteCount);
   }
   else
   {
      // this is the mandated order of the header fields
      addValue("v", "0" );
      addValue("o", "sipX 5 5 IN IP4 127.0.0.1");
      addValue("s");
      addValue("i");
      addValue("u");
      addValue("e");
      addValue("p");
      addValue("c");
      addValue("b");
   }
}

// Copy constructor
SdpBody::SdpBody(const SdpBody& rSdpBody) :
   HttpBody(rSdpBody)
{
   mClassType = SDP_BODY_CLASS;
   if(rSdpBody.sdpFields)
   {
      sdpFields = new UtlSList();

      NameValuePair* headerField;
      NameValuePair* copiedHeader = NULL;
      UtlSListIterator iterator((UtlSList&)(*(rSdpBody.sdpFields)));
      while((headerField = (NameValuePair*) iterator()))
      {
         copiedHeader = new NameValuePair(*headerField);
         sdpFields->append(copiedHeader);
      }
   }
   else
   {
      sdpFields = NULL;
   }
}

// Destructor
SdpBody::~SdpBody()
{
   if(sdpFields)
   {
      while(! sdpFields->isEmpty())
      {
         delete sdpFields->get();
      }
      delete sdpFields;
   }
}

/* ============================ MANIPULATORS ============================== */

void SdpBody::parseBody(const char* bodyBytes, int byteCount)
{
   if(byteCount < 0)
   {
      bodyLength = strlen(bodyBytes);
   }

   if(bodyBytes)
   {
      UtlString name;
      UtlString value;
      int nameFound;
      NameValuePair* nameValue;

      NameValueTokenizer parser(bodyBytes, byteCount);
      do
      {
         name.remove(0);
         value.remove(0);
         nameFound = parser.getNextPair(SDP_NAME_VALUE_DELIMITOR,
                                        &name, &value);
         if(nameFound)
         {
            nameValue = new NameValuePair(name.data(), value.data());
            sdpFields->append(nameValue);

         }
      }
      while(nameFound);
   }
}


// Assignment operator
SdpBody&
SdpBody::operator=(const SdpBody& rhs)
{
   if (this == &rhs)            // handle the assignment to self case
      return *this;

   // Copy the base class stuff
   this->HttpBody::operator=((const HttpBody&)rhs);

   if(sdpFields)
   {
      sdpFields->destroyAll();
   }

   if(rhs.sdpFields)
   {
      if(sdpFields == NULL)
      {
         sdpFields = new UtlSList();
      }

      NameValuePair* headerField;
      NameValuePair* copiedHeader = NULL;
      UtlSListIterator iterator((UtlSList&)(*(rhs.sdpFields)));
      while((headerField = (NameValuePair*) iterator()))
      {
         copiedHeader = new NameValuePair(*headerField);
         sdpFields->append(copiedHeader);
      }
   }

   // Set the class type just to play it safe
   mClassType = SDP_BODY_CLASS;
   return *this;
}

/* ============================ ACCESSORS ================================= */
void SdpBody::setStandardHeaderFields(const char* sessionName,
                                      const char* emailAddress,
                                      const char* phoneNumber,
                                      const char* originatorAddress)
{
   OsLock lock (sSessionLock) ;

   setOriginator("sipX", 5, sSessionCount++,
                 (originatorAddress && *originatorAddress) ?
                 originatorAddress : "127.0.0.1");
   setSessionNameField(sessionName);
   if(emailAddress && strlen(emailAddress) > 0)
   {
      setEmailAddressField(emailAddress);
   }
   if(phoneNumber && strlen(phoneNumber) > 0)
   {
      setPhoneNumberField(phoneNumber);
   }
}

void SdpBody::setSessionNameField(const char* sessionName)
{
   setValue("s", sessionName);
}

void SdpBody::setEmailAddressField(const char* emailAddress)
{
   setValue("e", emailAddress);
}

void SdpBody::setPhoneNumberField(const char* phoneNumber)
{
   setValue("p", phoneNumber);
}

void SdpBody::setValue(const char* name, const char* value)
{
   NameValuePair nvToMatch(name);
   NameValuePair* nvFound = NULL;
   UtlSListIterator iterator(*sdpFields);

   nvFound = (NameValuePair*) iterator.findNext(&nvToMatch);
   if(nvFound)
   {
      // field exists - replace the value
      nvFound->setValue(value);
   }
   else
   {
      addValue(name, value);
   }
}

int SdpBody::getMediaSetCount() const
{
   UtlSListIterator iterator(*sdpFields);
   NameValuePair mediaName("m");
   int count = 0;
   while(iterator.findNext(&mediaName))
   {
      count++;
   }
   return(count);

}

UtlBoolean SdpBody::getMediaType(int mediaIndex, UtlString* mediaType) const
{
   return(getMediaSubfield(mediaIndex, 0, mediaType));
}

UtlBoolean SdpBody::getMediaPort(int mediaIndex, int* port) const
{
   UtlString portString;
   UtlBoolean portFound = getMediaSubfield(mediaIndex, 1, &portString);
   int portCountSeparator;

   if(!portString.isNull())
   {
      // Remove the port pair count if it exists
      portCountSeparator = portString.index("/");
      if(portCountSeparator >= 0)
      {
         portString.remove(portCountSeparator);
      }

      *port = atoi(portString.data());
      portFound = TRUE;
   }

   return(portFound);
}

UtlBoolean SdpBody::getMediaRtcpPort(int mediaIndex, int* port) const
{
    UtlBoolean bFound = FALSE ;
    int iRtpPort ;
    
    if (getMediaPort(mediaIndex, &iRtpPort))
    {
        bFound = TRUE ;
        *port = iRtpPort + 1;

        UtlSListIterator iterator(*sdpFields);
        NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
        if(nv)
        {
            while ((nv = findFieldNameBefore(&iterator, "a", "m")))
            {
                //printf("->%s:%s\n", nv->data(), nv->getValue()) ;

                UtlString typeAttribute ;
                UtlString portAttribute ;

                NameValueTokenizer::getSubField(nv->getValue(), 0, 
                        ":", &typeAttribute) ;
                NameValueTokenizer::getSubField(nv->getValue(), 1,
                        ":", &portAttribute) ;

                if (typeAttribute.compareTo("rtcp", UtlString::ignoreCase) == 0)
                {
                    *port = atoi(portAttribute.data()) ;
                    
                }
            }
        }
    }

    return bFound ;
}

UtlBoolean SdpBody::getMediaProtocol(int mediaIndex, UtlString* transportProtocol) const
{
   return(getMediaSubfield(mediaIndex, 2, transportProtocol));
}

UtlBoolean SdpBody::getMediaPayloadType(int mediaIndex, int maxTypes,
                                        int* numTypes, int payloadTypes[]) const
{
    UtlString payloadTypeString;
    int typeCount = 0;
    int index = 0 ;

    while (index < maxTypes &&
            getMediaSubfield(mediaIndex, 3 + index++, &payloadTypeString))
    {
        if(!payloadTypeString.isNull())
        {
            // Add the payload type and increment typeCount if not 
            // already in the list
            bool bFound = false ;
            int payload = atoi(payloadTypeString.data()) ;
            for (int i=0; i<typeCount; i++)
            {
                if (payloadTypes[i] == payload)
                {
                    bFound = true ;
                    break ;
                }
            }

            if (!bFound)
            {            
                payloadTypes[typeCount] = payload ;
                typeCount++;
            }
        }
    }

    *numTypes = typeCount;

    return(typeCount > 0);
}

UtlBoolean SdpBody::getMediaSubfield(int mediaIndex, int subfieldIndex, UtlString* subField) const
{
   UtlBoolean subfieldFound = FALSE;
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
   const char* value;
   subField->remove(0);

   if(nv)
   {
      value =  nv->getValue();
      NameValueTokenizer::getSubField(value, subfieldIndex,
                                      SDP_SUBFIELD_SEPARATORS, subField);
      if(!subField->isNull())
      {
         subfieldFound = TRUE;
      }
   }
   return(subfieldFound);
}

UtlBoolean SdpBody::getPayloadRtpMap(int payloadType,
                                     UtlString& mimeSubtype,
                                     int& sampleRate,
                                     int& numChannels) const
{
   // an "a" record look something like:
   // "a=rtpmap:<payloadType> <mimeSubtype/sampleRate[/numChannels]"

   // Loop through all of the "a" records
   UtlBoolean foundRtpMap = FALSE;
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = NULL;
   int aFieldIndex = 0;
   const char* value;
   UtlString aFieldType;
   UtlString payloadString;
   UtlString sampleRateString;
   UtlString numChannelString;
   UtlString aFieldMatch("a");

   while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
   {
      value =  nv->getValue();

      // Verify this is an rtpmap "a" record
      NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                      &aFieldType);
      if(aFieldType.compareTo("rtpmap") == 0)
      {
         // If this is the rtpmap for the requested payload type
         NameValueTokenizer::getSubField(value, 1,
                                         " \t:/", // seperators
                                         &payloadString);
         if(atoi(payloadString.data()) == payloadType)
         {
            // The mime subtype is the 3nd subfield
            NameValueTokenizer::getSubField(value, 2,
                                            " \t:/", // seperators
                                            &mimeSubtype);

            // The sample rate is the 4rd subfield
            NameValueTokenizer::getSubField(value, 3,
                                            " \t:/", // seperators
                                            &sampleRateString);
            sampleRate = atoi(sampleRateString.data());
            if(sampleRate <= 0) sampleRate = -1;

            // The number of channels is the 5th subfield
            NameValueTokenizer::getSubField(value, 4,
                                            " \t:/", // seperators
                                            &numChannelString);
            numChannels = atoi(numChannelString.data());
            if(numChannels <= 0) numChannels = -1;

            // we are all done
            foundRtpMap = TRUE;
            break;
         }
      }

      aFieldIndex++;
   }
   return(foundRtpMap);
}

UtlBoolean SdpBody::getPayloadFormat(int payloadType,
                                     UtlString& fmtp,
                                     int& valueFmtp,
                                     int& numVideoSizes,
                                     int videoSizes[]) const
{
   // an "a" record look something like:
   // "a=fmtp:<payloadType> <fmtpdata>"

   // Loop through all of the "a" records
   UtlBoolean foundPayloadFmtp = FALSE;
   UtlBoolean foundField;
   UtlSListIterator iterator(*sdpFields);
   UtlString aFieldType;
   UtlString payloadString;
   UtlString modifierString;
   NameValuePair* nv = NULL;
   int index = 0;
   int aFieldIndex = 0;
   int videoIndex = 0;
   const char* value;
   UtlString temp;
   UtlString aFieldMatch("a");

   numVideoSizes = 0;
   valueFmtp = 0;

   while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
   {
      value =  nv->getValue();

      // Verify this is an fmtp "a" record
      NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                      &aFieldType);
      if(aFieldType.compareTo("fmtp") == 0)
      {
         NameValueTokenizer::getSubField(value, 1,
                                         " \t:/", // seperators
                                         &payloadString);
         if(atoi(payloadString.data()) == payloadType)
         {
            const char *fmtpSubField;
            int subFieldLen;
            // If this is the fmtp for the requested payload type
            foundField = NameValueTokenizer::getSubField(value, -1, 2, 
                                                         " \t:",  // seperators
                                                         fmtpSubField,
                                                         subFieldLen,
                                                         0);
            if(foundField) 
            {
               fmtp = fmtpSubField;
            }

            foundPayloadFmtp = TRUE;
            // Get modifier
            NameValueTokenizer::getSubField(value, 2,
                                                    " \t:=", // seperators
                                                    &modifierString);
            // Get value
            foundField = NameValueTokenizer::getSubField(value, 3,
                                                    " \t:=", // seperators
                                                    &temp);


            valueFmtp = 0;
            index = 3;
            if (modifierString.compareTo("mode") == 0)
            {
                valueFmtp  = atoi(temp.data());
            }
            else if (modifierString.compareTo("imagesize") == 0)
            {
                // Checking size information for imagesize modifier
                valueFmtp = atoi(temp.data());
                switch (valueFmtp)
                {
                case 0: 
                    videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QCIF;
                    break;
                case 1:
                    videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_CIF;
                    break;
                }
                valueFmtp = 0;
                numVideoSizes = videoIndex;
            }
            else if (modifierString.compareTo("size") == 0)
            {
                // Checking size information for other modifier
                while (foundField && index < 8 && videoIndex < MAXIMUM_VIDEO_SIZES)
                {
                    foundField = NameValueTokenizer::getSubField(value, index++,
                                                                " \t/:", // seperators
                                                                &temp);
                    if (temp.compareTo("CIF") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_CIF;
                    }
                    else if (temp.compareTo("QVGA") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QVGA;
                    }
                    else if (temp.compareTo("QCIF") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QCIF;
                    }
                    else if (temp.compareTo("SQCIF") == 0)
                    {
                        videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_SQCIF;
                    }
                }
                numVideoSizes = videoIndex;
             }
             if (videoIndex == 0)
             {
                 // Default to QCIF if no size is present
                 videoSizes[videoIndex++] = SDP_VIDEO_FORMAT_QCIF;
                 numVideoSizes = 1;
             }
         }
      }

      aFieldIndex++;
   }
   return(foundPayloadFmtp);
}

UtlBoolean SdpBody::getSrtpCryptoField(int mediaIndex,
                                       int index,
                                       SdpSrtpParameters& params) const
{
    UtlBoolean foundCrypto = FALSE;
    UtlBoolean foundField;
    UtlString aFieldType;
    UtlSListIterator iterator(*sdpFields);
    NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
    const char* value;
    UtlString indexString;
    UtlString cryptoSuite;
    UtlString temp;
    int size;
    char srtpKey[SRTP_KEY_LENGTH+1];

    size = sdpFields->entries();
    while ((nv = findFieldNameBefore(&iterator, "a", "m")))
    {
        value =  nv->getValue();

        // Verify this is an crypto "a" record
        NameValueTokenizer::getSubField(value, 0,
                                        " \t:/", // seperators
                                        &aFieldType);
        if(aFieldType.compareTo("crypto") == 0)
        {
            NameValueTokenizer::getSubField(value, 1,
                                            " \t:/", // seperators
                                            &indexString);
            if(atoi(indexString.data()) == index)
            {
                foundCrypto = TRUE;

                // Encryption & authentication on by default
                params.securityLevel = SRTP_ENCRYPTION | SRTP_AUTHENTICATION;

                NameValueTokenizer::getSubField(value, 2,
                                                " \t:/", // seperators
                                                &cryptoSuite);
                // Check the crypto suite 
                if (cryptoSuite.compareTo("AES_CM_128_HMAC_SHA1_80") == 0)
                {
                    params.cipherType = AES_CM_128_HMAC_SHA1_80;
                }
                else if (cryptoSuite.compareTo("AES_CM_128_HMAC_SHA1_32") == 0)
                {
                    params.cipherType = AES_CM_128_HMAC_SHA1_32;
                }
                else if (cryptoSuite.compareTo("F8_128_HMAC_SHA1_80") == 0)
                {
                    params.cipherType = F8_128_HMAC_SHA1_80;
                }
                else
                {
                    //Couldn't find crypto suite, no secritiy
                    params.securityLevel = 0;
                }

                // Get key
                foundField = NameValueTokenizer::getSubField(value, 4,
                                                             " \t/:|", // seperators
                                                             &temp);
                NetBase64Codec::decode(temp.length(), temp.data(), size, srtpKey);
                if (size <= SRTP_KEY_LENGTH)
                {
                    srtpKey[size] = 0;
                }
                else
                {
                    srtpKey[SRTP_KEY_LENGTH] = 0;
                }
                strcpy((char*)params.masterKey, srtpKey);

                // Modify security level with session parameters
                for (int index=5; foundField; ++index)
                {
                    foundField = NameValueTokenizer::getSubField(value, index,
                                                                 " \t/:|", // seperators
                                                                 &temp);
                    if (foundField)
                    {
                        if (temp.compareTo("UNENCRYPTED_SRTP") == 0)
                        {
                            params.securityLevel &= ~SRTP_ENCRYPTION;
                        }
                        if (temp.compareTo("UNAUTHENTICATED_SRTP") == 0)
                        {
                            params.securityLevel &= ~SRTP_AUTHENTICATION;
                        }
                    }
                }
                break;
            }
        }
    }
    return foundCrypto;
}

UtlBoolean SdpBody::getFramerateField(int mediaIndex,
                                      int& videoFramerate) const
{
    UtlBoolean foundFramerate = FALSE;
    UtlString aFieldType;
    UtlSListIterator iterator(*sdpFields);
    NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
    const char* value;
    UtlString aFieldMatch("a");
    UtlString rateString;
    int size;
    UtlString temp;
    videoFramerate = 0;

    size = sdpFields->entries();
    while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
    {
        value =  nv->getValue();

        // Verify this is an crypto "a" record
        NameValueTokenizer::getSubField(value, 0,
                                        " \t:/", // seperators
                                        &aFieldType);
        if(aFieldType.compareTo("framerate") == 0)
        {
            NameValueTokenizer::getSubField(value, 1,
                                            " \t:/", // seperators
                                            &rateString);
            videoFramerate = atoi(rateString.data());
            foundFramerate = TRUE;
        }
    }
    return foundFramerate;
}

UtlBoolean SdpBody::getBandwidthField(int& bandwidth) const
{
    UtlBoolean bFound = FALSE;

   // Loop through all of the "b" records
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = NULL;
   const char* value;
   UtlString aFieldMatch("b");
   UtlString aFieldModifier;
   UtlString temp;

   // Indicate no "b" field was sent with 0
   bandwidth  = 0;

   while((nv = (NameValuePair*) iterator.findNext(&aFieldMatch)) != NULL)
   {
      value =  nv->getValue();

      // Verify this is an crypto "a" record
      NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                        &aFieldModifier);
      if(aFieldModifier.compareTo("CT") == 0)
      {
            NameValueTokenizer::getSubField(value, 1,
                                            " \t:/", // seperators
                                            &temp);

            bandwidth = atoi(temp.data());
            bFound = TRUE;
      }
   }
   return bFound;
}

UtlBoolean SdpBody::getValue(int fieldIndex, UtlString* name, UtlString* value) const
{
   NameValuePair* nv = NULL;
   name->remove(0);
   value->remove(0);
   if(fieldIndex >=0)
   {
      nv = (NameValuePair*) sdpFields->at(fieldIndex);
      if(nv)
      {
         *name = *nv;
         *value = nv->getValue();
      }
   }
   return(nv != NULL);
}

UtlBoolean SdpBody::getMediaData(int mediaIndex, UtlString* mediaType,
                                 int* mediaPort, int* mediaPortPairs,
                                 UtlString* mediaTransportType,
                                 int maxPayloadTypes, int* numPayloadTypes,
                                 int payloadTypes[]) const
{
   UtlBoolean fieldFound = FALSE;
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv = positionFieldInstance(mediaIndex, &iterator, "m");
   const char* value;
   UtlString portString;
   UtlString portPairString;
   int portCountSeparator;
   int typeCount = 0;
   UtlString payloadTypeString;

   if(nv)
   {
      fieldFound = TRUE;
      value =  nv->getValue();

      // media Type
      NameValueTokenizer::getSubField(value, 0,
                                      SDP_SUBFIELD_SEPARATORS, mediaType);

      // media port and media port pair count
      NameValueTokenizer::getSubField(value, 1,
                                      SDP_SUBFIELD_SEPARATORS, &portString);
      if(!portString.isNull())
      {
         // Copy for obtaining the pair count
         portPairString.append(portString);

         // Remove the port pair count if it exists
         portCountSeparator = portString.index("/");
         if(portCountSeparator >= 0)
         {
            portString.remove(portCountSeparator);

            // Get the other part of the field
            portPairString.remove(0, portCountSeparator + 1);
         }
         else
         {
            portPairString.remove(0);
         }

         *mediaPort = atoi(portString.data());
         if(portPairString.isNull())
         {
            *mediaPortPairs = 1;
         }
         else
         {
            *mediaPortPairs = atoi(portPairString.data());
         }
      }
      else
      {
         *mediaPort = 0;
         *mediaPortPairs = 0;
      }

      // media transport type
      NameValueTokenizer::getSubField(value, 2,
                                      SDP_SUBFIELD_SEPARATORS, mediaTransportType);

      // media payload/codec types
      NameValueTokenizer::getSubField(value,  3 + typeCount,
                                      SDP_SUBFIELD_SEPARATORS, &payloadTypeString);
      while(typeCount < maxPayloadTypes &&
            !payloadTypeString.isNull())
      {
         payloadTypes[typeCount] = atoi(payloadTypeString.data());
         typeCount++;
         NameValueTokenizer::getSubField(value,  3 + typeCount,
                                         SDP_SUBFIELD_SEPARATORS, &payloadTypeString);
      }
      *numPayloadTypes = typeCount;
   }

   return(fieldFound);
}

int SdpBody::findMediaType(const char* mediaType, int startMediaIndex) const
{
   NameValuePair* mediaField;
   UtlSListIterator iterator(*sdpFields);
   UtlBoolean mediaTypeFound = FALSE;
   int index = startMediaIndex;
   NameValuePair mediaName("m");
   mediaField = positionFieldInstance(startMediaIndex, &iterator, "m");
   const char* value;

   while(mediaField && ! mediaTypeFound)
   {
      value = mediaField->getValue();
      if(strstr(value, mediaType) == value)
      {
         mediaTypeFound = TRUE;
         break;
      }

      mediaField = (NameValuePair*) iterator.findNext(&mediaName);
      index++;
   }

   if(! mediaTypeFound)
   {
      index = -1;
   }
   return(index);
}

UtlBoolean SdpBody::getMediaAddress(int mediaIndex, UtlString* address) const
{
   UtlSListIterator iterator(*sdpFields);
   NameValuePair* nv;
   address->remove(0);
   const char* value = NULL;
   int ttlIndex;

   // Try to find a specific address for the given media set
   nv = positionFieldInstance(mediaIndex, &iterator, "m");
   if(nv)
   {
      nv = findFieldNameBefore(&iterator, "c", "m");
      if(nv)
      {
         value = nv->getValue();
         if(value)
         {
            NameValueTokenizer::getSubField(value, 2,
                                            SDP_SUBFIELD_SEPARATORS, address);
         }
      }

      // Did not find a specific address try to find the default
      if(address->isNull())
      {
         iterator.reset();
         nv = findFieldNameBefore(&iterator, "c", "m");

         // Default address exists in the header
         if(nv)
         {
            value = nv->getValue();
            if(value)
            {
               NameValueTokenizer::getSubField(value, 2,
                                               SDP_SUBFIELD_SEPARATORS, address);
            }
         }
      }

      if(!address->isNull())
      {
         // Check if there is a time to live attribute and remove it
         ttlIndex = address->index("/");
         if(ttlIndex >= 0)
         {
            address->remove(ttlIndex);
         }
      }
   }

   return(!address->isNull());
}

UtlBoolean SdpBody::getPtime(int mediaIndex, int& pTime) const
{
    UtlBoolean foundPtime = FALSE;
    UtlSListIterator iterator(*sdpFields);
    NameValuePair* nv;
    pTime = 0;
    const char* value = NULL;

    // Try to find a specific address for the given media set
    nv = positionFieldInstance(mediaIndex, &iterator, "m");
    if(nv)
    {
        UtlString aParameterName;
        UtlString pTimeValueString;
        while((nv = findFieldNameBefore(&iterator, "a", "m")))
        {
            value = nv->getValue();
            if(value)
            {
                // Get the a line parameter name
                NameValueTokenizer::getSubField(value, 0,
                                      " \t:/", // seperators
                                      &aParameterName);
                // See if this is a ptime parameter
                if(aParameterName.compareTo("ptime") == 0)
                {
                     // get the ptime value
                    NameValueTokenizer::getSubField(value, 1,
                                                    " \t:/", // seperators, 
                                                    &pTimeValueString);
                    if(!pTimeValueString.isNull())
                    {
                        pTime = atoi(pTimeValueString);
                        // should only be one ptime per media set (m line)
                        // Ignore all but the first one.
                        if(pTime > 0)
                        {
                            foundPtime = TRUE;
                            break;
                        }
                    }
                }
            }
        }
    }
    return(foundPtime);
}

void SdpBody::getBestAudioCodecs(int numRtpCodecs, SdpCodec rtpCodecs[],
                                 UtlString* rtpAddress, int* rtpPort,
                                 int* sendCodecIndex,
                                 int* receiveCodecIndex) const
{

   int mediaIndex = 0;
   UtlBoolean sendCodecFound = FALSE;
   UtlBoolean receiveCodecFound = FALSE;
   int numTypes;
   int payloadTypes[MAXIMUM_MEDIA_TYPES];
   int typeIndex;
   int codecIndex;

   rtpAddress->remove(0);
   *rtpPort = 0;
   *sendCodecIndex = -1;
   *receiveCodecIndex = -1;

   while(mediaIndex >= 0 &&
         (!sendCodecFound || !receiveCodecFound))
   {
      mediaIndex = findMediaType(SDP_AUDIO_MEDIA_TYPE, mediaIndex);

      if(mediaIndex >= 0)
      {
         getMediaPort(mediaIndex, rtpPort);

         if(*rtpPort >= 0)
         {
            getMediaPayloadType(mediaIndex, MAXIMUM_MEDIA_TYPES,
                                &numTypes, payloadTypes);
            for(typeIndex = 0; typeIndex < numTypes; typeIndex++)
            {
               // Until the real SdpCodec is needed we assume all of
               // the rtpCodecs are send AND receive.
               // We are also going to cheat and assume that all of
               // the media records are send AND receive
               for(codecIndex = 0; codecIndex < numRtpCodecs; codecIndex++)
               {
                  if(payloadTypes[typeIndex] ==
                     (rtpCodecs[codecIndex]).getCodecType())
                  {
                     sendCodecFound = TRUE;
                     receiveCodecFound = TRUE;
                     *sendCodecIndex = codecIndex;
                     *receiveCodecIndex = codecIndex;
                     getMediaAddress(mediaIndex, rtpAddress);
                     getMediaPort(mediaIndex, rtpPort);
                     break;
                  }
               }
               if(sendCodecFound && receiveCodecFound)
               {
                  break;
               }
            }
         }
         mediaIndex++;
      }
   }
}


void SdpBody::getBestAudioCodecs(SdpCodecFactory& localRtpCodecs,
                                 int& numCodecsInCommon,
                                 SdpCodec**& codecsInCommonArray,
                                 UtlString& rtpAddress, 
                                 int& rtpPort,
                                 int& rtcpPort,
                                 int& videoRtpPort,
                                 int& videoRtcpPort,
                                 SdpSrtpParameters& localSrtpParams,
                                 SdpSrtpParameters& matchingSrtpParams,
                                 int localBandwidth,
                                 int& matchingBandwidth,
                                 int localVideoFramerate,
                                 int& matchingVideoFramerate) const
{

   int mediaAudioIndex = 0;
   int mediaVideoIndex = 0;
   int numAudioTypes;
   int numVideoTypes;
   int bandwidth;
   int framerate;
   int audioPayloadTypes[MAXIMUM_MEDIA_TYPES];
   int videoPayloadTypes[MAXIMUM_MEDIA_TYPES];
   numCodecsInCommon = 0;
   codecsInCommonArray = new SdpCodec*[localRtpCodecs.getCodecCount()];
   SdpSrtpParameters remoteSrtpParams;

   memset((void*)&remoteSrtpParams, 0, sizeof(SdpSrtpParameters));
   memset((void*)&matchingSrtpParams, 0, sizeof(SdpSrtpParameters));
   rtpAddress.remove(0);
   rtpPort = 0;
   videoRtpPort = 0 ;

   while(mediaAudioIndex >= 0 || mediaVideoIndex >=0)
   {
      mediaAudioIndex = findMediaType(SDP_AUDIO_MEDIA_TYPE, mediaAudioIndex);
      mediaVideoIndex = findMediaType(SDP_VIDEO_MEDIA_TYPE, mediaVideoIndex);

      getBandwidthField(bandwidth);
      getBandwidthInCommon(localBandwidth, bandwidth, matchingBandwidth);

      getFramerateField(mediaVideoIndex, framerate);
      getVideoFramerateInCommon(localVideoFramerate, framerate, matchingVideoFramerate);

      if(mediaAudioIndex >= 0)
      {
         // This is kind of a bad assumption if there is more
         // than one media field, each might have a different
         // port and address
         getMediaPort(mediaAudioIndex, &rtpPort);
         getMediaRtcpPort(mediaAudioIndex, &rtcpPort);
         getSrtpCryptoField(mediaAudioIndex, 1, remoteSrtpParams);
         getMediaPort(mediaVideoIndex, &videoRtpPort);
         getMediaRtcpPort(mediaVideoIndex, &videoRtcpPort);
         getMediaAddress(mediaAudioIndex, &rtpAddress);

         if(rtpPort >= 0)
         {
            getMediaPayloadType(mediaAudioIndex, MAXIMUM_MEDIA_TYPES,
                                &numAudioTypes, audioPayloadTypes);

            getMediaPayloadType(mediaVideoIndex, MAXIMUM_MEDIA_TYPES,
                                &numVideoTypes, videoPayloadTypes);

            getCodecsInCommon(numAudioTypes,
                              numVideoTypes,
                              audioPayloadTypes,
                              videoPayloadTypes,
                              videoRtpPort,
                              localRtpCodecs,
                              numCodecsInCommon,
                              codecsInCommonArray);

            getEncryptionInCommon(localSrtpParams, remoteSrtpParams, matchingSrtpParams);

            if (numCodecsInCommon >0)
               break;
         }
         mediaAudioIndex++;
         mediaVideoIndex++;
      }
   }
}


void SdpBody::getEncryptionInCommon(SdpSrtpParameters& audioParams,
                                    SdpSrtpParameters& remoteParams,
                                    SdpSrtpParameters& commonAudioParams) const
{
    memset(&commonAudioParams, 0, sizeof(SdpSrtpParameters));

    // Test for same cipher type and remote encryption being on
    if (audioParams.cipherType == remoteParams.cipherType && remoteParams.securityLevel)
    {
        // If we agree on a cipherType then the caller controls
        // if encryption is on - even if we switched it off locally.
        memcpy(&commonAudioParams, &remoteParams, sizeof(SdpSrtpParameters));
        // Set encryption on sending and receiving - if we can figure out
        // how to negotiate this we can be more selective
        commonAudioParams.securityLevel |= SRTP_SEND | SRTP_RECEIVE;
    }
}


void SdpBody::getBandwidthInCommon(int localBandwidth,
                                   int remoteBandwidth,
                                   int& commonBandwidth) const
{
    if (localBandwidth != 0 && remoteBandwidth != 0)
    {
        commonBandwidth = (remoteBandwidth <= localBandwidth) ? 
                                remoteBandwidth : localBandwidth;
    }
    else if (remoteBandwidth != 0)
    { 
        commonBandwidth = remoteBandwidth;
    }
    else
    {
        commonBandwidth = localBandwidth;
    }
}

void SdpBody::getVideoFramerateInCommon(int localVideoFramerate,
                                        int remoteVideoFramerate,
                                        int& commonVideoFramerate) const
{
    if (remoteVideoFramerate != 0 && remoteVideoFramerate < localVideoFramerate)
    {
        commonVideoFramerate = remoteVideoFramerate;
    }
    else
    {
        commonVideoFramerate = localVideoFramerate;
    }
}

void SdpBody::getCodecsInCommon(int audioPayloadIdCount,
                                int videoPayloadIdCount,
                                int audioPayloadTypes[],
                                int videoPayloadTypes[],
                                int videoRtpPort,
                                SdpCodecFactory& localRtpCodecs,
                                int& numCodecsInCommon,
                                SdpCodec* codecsInCommonArray[]) const
{
   UtlString mimeSubtype;
   UtlString fmtp;
   int sampleRate;
   int numChannels;
   const SdpCodec* matchingCodec = NULL;
   int typeIndex;
   UtlBoolean commonCodec;
   int videoSizes[MAXIMUM_VIDEO_SIZES];
   int numVideoSizes;

   numCodecsInCommon = 0;
   memset((void*)videoSizes, 0, sizeof(videoSizes));

   // TODO: properly support media sets
   // This is a bit of a mess.  We have completely blurred over the
   // concept of separate m lines other than the mime type separation
   // of audio and video.  There may be different properties for the
   // same codecs which may appear in more than one media section
   // (m line)

   // So for example the ptime property is specific to a media
   // set (m line).  At this point we no longer know which codec
   // comes from which media set.  So for now we search through the
   // audio media sets until we find a ptime and assume the ptime
   // applies to all audio codecs.  ptime mostly only make sense for
   // audio
   int mediaSetIndex = 0;
   int defaultPtime = 0;
   UtlString mediaType;
   while(getMediaType(mediaSetIndex, &mediaType))
   {
       if(mediaType.compareTo(MIME_TYPE_AUDIO) == 0)
       {
           if(getPtime(mediaSetIndex, defaultPtime) &&
               defaultPtime > 0)
           {
               break;
           }
       }
       mediaSetIndex++;
   }

   for(typeIndex = 0; typeIndex < audioPayloadIdCount; typeIndex++)
   {
      // Until the real SdpCodec is needed we assume all of
      // the rtpCodecs are send AND receive.
      // We are also going to cheat and assume that all of
      // the media records are send AND receive

      // Get the rtpmap for the payload type
      if(getPayloadRtpMap(audioPayloadTypes[typeIndex],
                          mimeSubtype, sampleRate, numChannels))
      {
         
         // Find a match for the mime type
         matchingCodec = localRtpCodecs.getCodec(MIME_TYPE_AUDIO, mimeSubtype.data());
         if((matchingCodec != NULL) &&
            (matchingCodec->getSampleRate() == sampleRate ||
             sampleRate == -1) &&
            (matchingCodec->getNumChannels() == numChannels ||
             numChannels == -1))
         {
            commonCodec = TRUE;
            int frameSize = 0;

            if (matchingCodec->getCodecType() == SdpCodec::SDP_CODEC_ILBC)
            {
                getPayloadFormat(audioPayloadTypes[typeIndex], fmtp, frameSize, 
                                 numVideoSizes, videoSizes);
                if (frameSize == 20 || frameSize == 30 || frameSize == 0)
                {
                   if (frameSize == 0)
                   {
                       frameSize = 20;
                   }

                }
                else
                {
                    // Nothing in common here
                    commonCodec = FALSE;
                }
            }
            else if(defaultPtime > 0)
            {
                frameSize = defaultPtime;
            }

            // Create a copy of the SDP codec and set
            // the payload type for it
            if (commonCodec)
            {
               codecsInCommonArray[numCodecsInCommon] =
                  new SdpCodec(*matchingCodec);
               codecsInCommonArray[numCodecsInCommon]->setCodecPayloadFormat(audioPayloadTypes[typeIndex]);
               if (frameSize)
               {
                  codecsInCommonArray[numCodecsInCommon]->setPacketSize(frameSize*1000);
               }

            }
         
            numCodecsInCommon++;

         }
      }

      // If no payload type set and this is a static payload
      // type assume the payload type is the same as our internal
      // codec id
      else if(audioPayloadTypes[typeIndex] <=
              SdpCodec::SDP_CODEC_MAXIMUM_STATIC_CODEC)
      {
         if((matchingCodec = localRtpCodecs.getCodecByType(audioPayloadTypes[typeIndex])))
         {
            // Create a copy of the SDP codec and set
            // the payload type for it
            codecsInCommonArray[numCodecsInCommon] =
               new SdpCodec(*matchingCodec);
            codecsInCommonArray[numCodecsInCommon]->setCodecPayloadFormat(audioPayloadTypes[typeIndex]);

            // Set the preferred frame size if ptime was set
            if (defaultPtime > 0)
            {
               codecsInCommonArray[numCodecsInCommon]->setPacketSize(defaultPtime*1000);
            }

            numCodecsInCommon++;
         }
      }
   }
   if (videoRtpPort != 0)
   {
        for(typeIndex = 0; typeIndex < videoPayloadIdCount; typeIndex++)
        {
            int videoFmtp = 0;
            SdpCodec** sdpCodecArray;
            int numCodecs;
            int codecIndex;
            int videoSize;
            // Until the real SdpCodec is needed we assume all of
            // the rtpCodecs are send AND receive.
            // We are also going to cheat and assume that all of
{'fraction_non_alphanumeric': 0.09813699661272111, 'fraction_numerical': 0.00626881821603312, 'mean_word_length': 1.5366967002804464, 'pattern_counts': {'":': 4, '<': 69, '<?xml version=': 0, '>': 161, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 298, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6623022', 'n_tokens_mistral': 23785, 'n_tokens_neox': 22814, 'n_words': 4940}