/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: sdpmdgen.cpp,v 1.14.2.1 2004/07/09 02:05:16 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "sdpmdgen.h"

#include "sdptypes.h"
#include "sdppyldinfo.h"
#include "hxstrutl.h"
#include "hxstring.h"
#include "nptime.h"
#include "netbyte.h"
#include "rtsputil.h"
#include "rtptypes.h"
//#include "hlxclib/stdio.h"

SDPMediaDescGenerator::SDPMediaDescGenerator(ULONG32 ulVersion) :
    m_pContext(0),
    m_pCCF(0),
    m_pRegistry(0),
    m_ulVersion(ulVersion),
    m_bUseOldEOL(FALSE),
    m_bUseSessionGUID(FALSE),
    m_bUseAbsoluteURL(FALSE)
{}

SDPMediaDescGenerator::~SDPMediaDescGenerator()
{
    HX_RELEASE(m_pCCF);
    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pRegistry);
}

HX_RESULT SDPMediaDescGenerator::Init(IUnknown* pContext)
{
    HX_RESULT res = HXR_FAILED;

    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pCCF);

    if (pContext)
    {
        pContext->QueryInterface(IID_IHXRegistry, (void**)&m_pRegistry);

        res = pContext->QueryInterface(IID_IHXCommonClassFactory,
                                       (void**)&m_pCCF);
        if (SUCCEEDED(res) && m_pCCF)
        {
            m_pContext = pContext;
            m_pContext->AddRef();
        }
    }

    return res;
}

HX_RESULT SDPMediaDescGenerator::Generate(UINT16 nValues, 
                                          IHXValues** pValueArray,
                                          REF(IHXBuffer*) pDescription)
{
    CHXString mDesc;

    char pszEOL[3]; /* Flawfinder: ignore */
    HX_RESULT rc;
    UINT32 propValue;
    const char* pPropName = 0;
    IHXBuffer* pPropBuffer = 0;
    char psz256[256]; /* Flawfinder: ignore */

    mDesc.SetMinBufSize(12000);

    if (m_bUseOldEOL)
    {
        pszEOL[0] = '\n';
        pszEOL[1] = '\0';
    }
    else
    {
        pszEOL[0] = '\r';
        pszEOL[1] = '\n';
        pszEOL[2] = '\0';
    }

    rc = SpecComplianceCheck(nValues, pValueArray);    
    if (HXR_OK != rc)
    {
        return rc;
    }
    // get local host address from registry

    //HX_RESULT hresult;
    IHXRegistry* pRegistry = 0;

    /*  XXX 
     *  Interop:  Our old system is expecting a media type in m= to be either
     *  "audio", "video", or "data" and anything else will be ignored.  
     *  The spec dectates otherwise...It should be whatever the type of mimetype
     *  is.  So, depending on the setting, we will have to change how we create 
     *  m= line for interop and backward comp.
     *
     *  by default, we will do the spec complient way.
     */    
    BOOL bUseOldSdp = FALSE;
    IHXBuffer* pHostIBuf = NULL;

    if(m_pRegistry)
    {
        m_pRegistry->GetStrByName("server.sdpconfig.hostaddr", pHostIBuf);
    }

    const char* pszHost = "";

    if (pHostIBuf)
    {
        pszHost = (const char *)pHostIBuf->GetBuffer();
    }

    UINT32 ulHostAddr = HXinet_addr(pszHost);

    /*
     * default sessionID, else get LastModified from optional values
     */

    char pszVersion[40]; /* Flawfinder: ignore */
    UINT32 ulSessionIDBuffLen = 40;
    IHXBuffer* pSessionGUIDBuff = NULL;
    IHXBuffer* pControlStringBuff = NULL;

    if(m_bUseSessionGUID && pValueArray[1])
    {
        rc = pValueArray[1]->GetPropertyCString("SessionGUID", pSessionGUIDBuff);

        if((rc == HXR_OK) && pSessionGUIDBuff)
        {
            ulSessionIDBuffLen = pSessionGUIDBuff->GetSize();
        }
    }
    NEW_FAST_TEMP_STR(pszSessionID, 256, ulSessionIDBuffLen);

    // munge current time with IP
    NPTime timeNow;
    if (!pSessionGUIDBuff)
    {
        SafeSprintf(pszSessionID,ulSessionIDBuffLen,"%lu", ulHostAddr + timeNow.m_lMicroSecond); 
    }

    SafeSprintf(pszVersion, 40, "%lu", timeNow.m_lMicroSecond); 

    IHXBuffer* pContentBase = NULL;
    IHXBuffer* pQueryParams = NULL;
    if(pValueArray[1])
    {
        ULONG32 ulLastMod;

        rc = pValueArray[1]->GetPropertyULONG32("LastModified", ulLastMod);

        SafeSprintf(pszVersion, 40,"%lu", ulLastMod);

        if (pSessionGUIDBuff)
        {
            SafeSprintf(pszSessionID, ulSessionIDBuffLen, "%s", 
                     pSessionGUIDBuff->GetBuffer());
        }
        else
        {
            SafeSprintf(pszSessionID, ulSessionIDBuffLen,"%lu", ulLastMod);
        }

        if (m_bUseAbsoluteURL)
        {
            rc = pValueArray[1]->GetPropertyCString("AbsoluteBaseURL",
                                                    pContentBase);
            if (HXR_OK != rc)
            {
                HX_RELEASE(pContentBase);
            }
            
            rc = pValueArray[1]->GetPropertyCString("URLQueryParams",
                                                    pQueryParams);
            if (HXR_OK != rc)
            {
                HX_RELEASE(pQueryParams);
            }
        }

        rc = pValueArray[1]->GetPropertyCString("Control", pControlStringBuff);
        if(rc != HXR_OK)
        {
            HX_RELEASE(pControlStringBuff);
        }
    }

    NEW_FAST_TEMP_STR(pszTmpString, 256, strlen(pszSessionID) + 
                      strlen(pszVersion) + strlen(pszHost) + 64);

    SafeSprintf(pszTmpString, 256, "v=0%so=- %s %s IN IP4 %s%s",
        pszEOL, pszSessionID, pszVersion, pszHost, pszEOL);

    HX_RELEASE(pHostIBuf);
    HX_RELEASE(pSessionGUIDBuff);
    DELETE_FAST_TEMP_STR(pszSessionID);

    mDesc = pszTmpString;

    DELETE_FAST_TEMP_STR(pszTmpString);

    char* pszTitle = NULL;
    char* pszAuthor = NULL;
    char* pszCopyright = NULL;

    CHXString headerAttributes;
    headerAttributes.SetMinBufSize(8192);

    char* pszSDPData=NULL;
    char* pszInfo=NULL;
    
    char* pszConnAddr=NULL;

    UINT32 avgBitRate = 0;
    BOOL   bFoundAvgBitRate = FALSE;
    UINT32 connTTL = 0;
    BOOL   bFoundTTL = FALSE;
    UINT32 connRange = 0;
    BOOL   bFoundRange = FALSE;
    BOOL   bAddToHeader = 0;
    UINT32 ulDefaultDuration = 0;
    BOOL   bDefaultDurationFound = FALSE;
    BOOL   bIsLive = FALSE;
    
    /*
     * Add session-level a=control field only for clients that can handle
     * interop SDP.
     */

    // Figure out if the client can handle interop SDP
    UINT32 ulSdpFileType = NONE_SDP;
    if (HXR_OK == pValueArray[0]->GetPropertyULONG32("SdpFileType", ulSdpFileType))
    {
        // default for bUseOldSdp is FALSE
        if (BACKWARD_COMP_SDP == ulSdpFileType)
        {
            bUseOldSdp = TRUE;
        }
    }

    if(!bUseOldSdp) 
    {
        if (!pControlStringBuff)
        {
            const char* pszFieldPrefix = "a=control:";
            UINT32 ulBuffLen = 0;

            if (pContentBase)
            {
                char *pszBaseURL = (char*)pContentBase->GetBuffer();
                UINT32 ulBaseLen = strlen(pszBaseURL);
                UINT32 ulBuffLen = strlen(pszFieldPrefix) + ulBaseLen +
                                   ((pQueryParams) ? pQueryParams->GetSize() : 0);
                char cPathDelim = '\0';

                // Strip off the trailing slash from the aggregate control URL
                char *pszPathDelim = pszBaseURL + (ulBaseLen-1);
                if (*pszPathDelim == '/')
                {
                    cPathDelim = *pszPathDelim;
                    *pszPathDelim = '\0';
                }

                ulBuffLen += strlen(pszEOL) + 1;
                NEW_FAST_TEMP_STR(pszSessionCtrl, 512, ulBuffLen); 
                SafeSprintf(pszSessionCtrl, ulBuffLen, "%s%s%s%s", 
                         pszFieldPrefix, pszBaseURL,
                         (pQueryParams)?((char*)pQueryParams->GetBuffer()) : "",
                         pszEOL);

                if (cPathDelim)
                {
                    *pszPathDelim = cPathDelim;
                }
                headerAttributes += pszSessionCtrl;
                DELETE_FAST_TEMP_STR(pszSessionCtrl);
            }
            else 
            {
                ulBuffLen = strlen(pszFieldPrefix) + strlen(pszEOL) + 2;
                NEW_FAST_TEMP_STR(pszSessionCtrl, 512, ulBuffLen); 
                SafeSprintf(pszSessionCtrl, ulBuffLen, "%s*%s", pszFieldPrefix, pszEOL);
                headerAttributes += pszSessionCtrl;
                DELETE_FAST_TEMP_STR(pszSessionCtrl);
            }
        }
        else
        {
            UINT32 ulBuffLen = pControlStringBuff->GetSize();
            ulBuffLen += (pContentBase) ? pContentBase->GetSize() : 0;
            ulBuffLen += (pQueryParams) ? pQueryParams->GetSize() : 0;
            ulBuffLen += 64;

            NEW_FAST_TEMP_STR(pszControlString, 512, ulBuffLen); 
            SafeSprintf(pszControlString, ulBuffLen, "a=control:%s%s%s%s",
                     ((pContentBase) ? ((char*)pContentBase->GetBuffer()) : ""),
                     pControlStringBuff->GetBuffer(),
                     (pQueryParams) ?((char*)pQueryParams->GetBuffer()) : "", 
                     pszEOL);

            headerAttributes += pszControlString;
            DELETE_FAST_TEMP_STR(pszControlString);
        }
    }

    rc = pValueArray[0]->GetFirstPropertyULONG32(pPropName, propValue);

    while(rc == HXR_OK)
    {
        bAddToHeader = FALSE;

        if(strcasecmp(pPropName, "MulticastTTL") == 0)
        {
            connTTL = propValue;
            bFoundTTL = TRUE;
        }
        else if(strcasecmp(pPropName, "MulticastRange") == 0)
        {
            connRange = propValue;
            bFoundRange = TRUE;
        }
        else if(strcasecmp(pPropName, "Duration") == 0)
        {
            // save this off, so we can add an appropriate a=range depending
            // on the value in "SdpFileType"
            ulDefaultDuration = propValue;
            bDefaultDurationFound = TRUE;
        }
        else if(strcasecmp(pPropName, "LiveStream") == 0)
        {
            // is this live?
            bIsLive = propValue;
            bAddToHeader = TRUE;
        }
        else if(strcasecmp(pPropName, "AvgBitRate") == 0)
        {
            avgBitRate = propValue;

            // we need this in kbps
            avgBitRate = (UINT32)((double)avgBitRate / 1000.0 + 0.5);
            bFoundAvgBitRate = TRUE;

            // add it to attribute as well.         
            bAddToHeader = TRUE;    
        }
        else if(strcasecmp(pPropName, "SdpFileType") == 0)
        {
            // don't add it to attribute.
            bAddToHeader = FALSE;    
        }
        else
        {
            bAddToHeader = TRUE;    
        }

        if(bAddToHeader)
        {
            NEW_FAST_TEMP_STR(pszAttBuf, 256, strlen(pPropName) + 64);
            SafeSprintf(pszAttBuf, 256,"a=%s:integer;%d%s", pPropName, propValue,
                                                    pszEOL);
            headerAttributes += pszAttBuf;
            DELETE_FAST_TEMP_STR(pszAttBuf);
        }

        /* make sure to reset SdpFileType header */
        if (strcasecmp(pPropName, "SdpFileType") == 0)
        {
            pValueArray[0]->SetPropertyULONG32(pPropName, NONE_SDP);
        }

        rc = pValueArray[0]->GetNextPropertyULONG32(pPropName,
            propValue);
    }

    /* 
     * 3GPP compliance: session-level "b=" parameter is required.  If we
     * it's not in the file header, then compute it from component streams.
     */
    if (!bFoundAvgBitRate)
    {
        UINT32 ulSessionBitRate = 0;
        UINT32 ulStreamBitRate = 0;
        
        for(UINT16 i=2 ; i < nValues; ++i)
        {
            if (pValueArray[i])
            {
                if (HXR_OK == pValueArray[i]->GetPropertyULONG32("AvgBitRate",
                                                               ulStreamBitRate))
                {   
                    bFoundAvgBitRate = TRUE;
                    ulSessionBitRate += ulStreamBitRate;
                }
            }
        }

        if (bFoundAvgBitRate) 
        { 
            // Convert bits/sec -> Kbits/sec (round up to nearest whole num)
                avgBitRate = (UINT32) ((double)ulSessionBitRate / 1000.0 + 0.5);
        }
    }

    rc = pValueArray[0]->GetFirstPropertyBuffer(pPropName, pPropBuffer);
    while(rc == HXR_OK)
    {
        INT32 dataSize = pPropBuffer->GetSize();
        char* pString = (char*) pPropBuffer->GetBuffer();
        bAddToHeader = TRUE;         // add it to attribute by default

        if (dataSize > 0 && pString != NULL && *pString != '\0')
        {
            if(strcasecmp(pPropName, "Title") == 0)
            {
                pszTitle = new char [ dataSize + 1 ];
                memcpy(pszTitle, (const char*)pPropBuffer->GetBuffer(), dataSize); /* Flawfinder: ignore */
                pszTitle[dataSize] = '\0';
            }
            else if(strcasecmp(pPropName, "Author") == 0)
            {
                pszAuthor = new char [ dataSize + 1 ];
                memcpy(pszAuthor, (const char*)pPropBuffer->GetBuffer(), dataSize); /* Flawfinder: ignore */
                pszAuthor[dataSize] = '\0';
            }
            else if(strcasecmp(pPropName, "Copyright") == 0)
            {
                pszCopyright = new char [ dataSize + 1 ];
                memcpy(pszCopyright, (const char*)pPropBuffer->GetBuffer(), dataSize); /* Flawfinder: ignore */
                pszCopyright[dataSize] = '\0';
            }

            if (bAddToHeader)
            {
                NEW_FAST_TEMP_STR(pszAttBuf, 4096, dataSize * 2 + strlen(pPropName)
                                                   + 64);
                NEW_FAST_TEMP_STR(pszPropString, 4096, dataSize * 2 + 64);

                (void)BinTo64((const BYTE*)pPropBuffer->GetBuffer(),
                    dataSize, pszPropString);

                SafeSprintf(pszAttBuf, 4096, "a=%s:buffer;\"%s\"%s",pPropName, 
                                                          pszPropString, pszEOL);

                headerAttributes += pszAttBuf;
                DELETE_FAST_TEMP_STR(pszPropString);
                DELETE_FAST_TEMP_STR(pszAttBuf);
            }
        }

        HX_RELEASE(pPropBuffer);

        rc = pValueArray[0]->GetNextPropertyBuffer(pPropName,
            pPropBuffer);
    }

    rc = pValueArray[0]->GetFirstPropertyCString(pPropName,
        pPropBuffer);
    while(rc == HXR_OK)
    {
        char* pString = (char*) pPropBuffer->GetBuffer();
        char* pszData=NULL;
        BOOL bDeleteString=FALSE;

        bAddToHeader = FALSE;

        INT32 dataSize = pPropBuffer->GetSize();

        if (dataSize > 0 && pString != NULL && *pString != '\0')
        {
            if(strcasecmp(pPropName, "Title") == 0)
            {
                pszTitle = EscapeBuffer(pString, pPropBuffer->GetSize());
                pszData = pszTitle;

                bAddToHeader = TRUE;    
            }
            else if(strcasecmp(pPropName, "Author") == 0)
            {
                pszAuthor = EscapeBuffer(pString, pPropBuffer->GetSize());
                pszData = pszAuthor;

                bAddToHeader = TRUE;    
            }
            else if(strcasecmp(pPropName, "Copyright") == 0)
            {
                pszCopyright = EscapeBuffer(pString, pPropBuffer->GetSize());
                pszData = pszCopyright;

                bAddToHeader = TRUE;    
            }
            else if(strcasecmp(pPropName, "MulticastAddress") == 0)
            {
                pszConnAddr = EscapeBuffer(pString, pPropBuffer->GetSize());
            }
            else if(strcasecmp(pPropName, "SDPData") == 0)
            {
                pszSDPData = new char [ dataSize + 1 ];
                memcpy(pszSDPData, (const char*)pPropBuffer->GetBuffer(), dataSize); /* Flawfinder: ignore */
                pszSDPData[dataSize] = '\0';
		RemoveASLine(pszSDPData, dataSize);
            }   
            else if(strcasecmp(pPropName, "Information") == 0)
            {
                // "i="
                pszInfo = EscapeBuffer(pString, pPropBuffer->GetSize());
            }
            else
            {
                pszData = EscapeBuffer(pString, pPropBuffer->GetSize());
                bAddToHeader = TRUE;
                bDeleteString = TRUE;
            }

            if(bAddToHeader)
            {
                HX_ASSERT(pszData);
                NEW_FAST_TEMP_STR(pszAttBuf, 4096, dataSize +
                                                   strlen(pPropName) + 64);
                SafeSprintf(pszAttBuf, 4096,"a=%s:string;\"%s\"%s", pPropName, 
                                                          pszData, pszEOL);
                headerAttributes += pszAttBuf;
                DELETE_FAST_TEMP_STR(pszAttBuf);
            }

            if (bDeleteString)
            {
                HX_VECTOR_DELETE(pszData);
            }
        }
        
        HX_RELEASE(pPropBuffer);
        rc = pValueArray[0]->GetNextPropertyCString(pPropName,
                                                    pPropBuffer);
    }


    /*
     *  Things in headerAttributes that depend on the value in "SdpFileType"
     */
    if (!bIsLive)
    {
        if (bDefaultDurationFound)
        {
                NPTime npTime(ulDefaultDuration);

            const char* pszTime = (const char*)npTime;
            headerAttributes += "a=range:npt=0-";
            headerAttributes += pszTime;
            headerAttributes += pszEOL;
        }               
        else
        {
            /*
             * error case - SpecComplianceCheck() should be taking care of it.
             *
             * let's just treat it as live.  When file is done, RTCP_BYE
             * will be sent, and client TEARDOWN
             */
                if (bUseOldSdp)
                {
                    SafeSprintf(psz256, 256, "a=range:npt=0-0%s", pszEOL); 
                        headerAttributes += psz256;             
                }
                else
                {
                    SafeSprintf(psz256, 256,"a=range:npt=0-%s", pszEOL); 
                        headerAttributes += psz256;             
                }
        }
    }   
     

    if (!pszTitle)
    {
       SafeSprintf(psz256, 256,"s=<No title>%s", pszEOL); 
       mDesc += psz256;
    }
    else
    {
        NEW_FAST_TEMP_STR(pszTmpStr, 256, strlen(pszTitle) + 64);
        SafeSprintf(pszTmpStr, 256, "s=%s%s", pszTitle, pszEOL); 
        mDesc += pszTmpStr;
        DELETE_FAST_TEMP_STR(pszTmpStr);
    }

    if (pszInfo)
    {
        NEW_FAST_TEMP_STR(pszTmpStr, 256, strlen(pszInfo) + 64);
        SafeSprintf(pszTmpStr, 256,"i=%s%s", pszInfo, pszEOL); 
        mDesc += pszTmpStr;
        DELETE_FAST_TEMP_STR(pszTmpStr);

        HX_VECTOR_DELETE(pszInfo);
        pszInfo = NULL;
    }
    else
    {
        const char* pszDefaultAuthor = "<No author>";
        const char* pszDefaultCopyright = "<No copyright>";
        UINT32 len = 64;

        len += pszAuthor ? strlen(pszAuthor) : 16;
        len += pszCopyright ? strlen(pszCopyright) : 16;

        NEW_FAST_TEMP_STR(pszTmpString, 512, len);

        SafeSprintf(pszTmpString, 256, "i=%s %s%s",
            pszAuthor ? pszAuthor : pszDefaultAuthor, 
            pszCopyright ? pszCopyright : pszDefaultCopyright,
            pszEOL);

        mDesc += pszTmpString;
        DELETE_FAST_TEMP_STR(pszTmpString);
    }

    if (pszTitle)
        HX_VECTOR_DELETE(pszTitle);

    if (pszAuthor)
        HX_VECTOR_DELETE(pszAuthor);

    if (pszCopyright)
        HX_VECTOR_DELETE(pszCopyright);

    /* Format the connection line only if MulticastAddress and MulticastTTL
     * exist in the file header
     */
    if (pszConnAddr && strlen(pszConnAddr) && bFoundTTL)
    {
        NEW_FAST_TEMP_STR(pszTmpString, 256, strlen(pszConnAddr) + 64);
        if (bFoundRange)
        {
            SafeSprintf(pszTmpString,256, "c=IN IP4 %s/%d/%d%s", 
                pszConnAddr, connTTL, connRange, pszEOL);
        }
        else
        {
            SafeSprintf(pszTmpString, 256, "c=IN IP4 %s/%d%s",
                pszConnAddr, connTTL, pszEOL);
        }

        mDesc += pszTmpString;

        DELETE_FAST_TEMP_STR(pszTmpString);
    }
    else if (!bUseOldSdp)
    {   
        // we still need a c= line with a NULL value (RFC 2326 C.1.7)
        // XXXGo
        // Since adding this line w/o TTL (i.e. 0.0.0.0/ttl) will cause 
        // older sdpplin to skip the next line (due to a wrong parsing), 
        // add this only when we are using new sdp type.
        mDesc += "c=IN IP4 0.0.0.0";
        mDesc += pszEOL;
    }    

    if (bFoundAvgBitRate)
    {
        SafeSprintf(psz256, 256, "b=AS:%u%s", avgBitRate, pszEOL); 
        mDesc += psz256;
        bFoundAvgBitRate = FALSE;
    }

    mDesc += "t=0 0";
    mDesc += pszEOL;

    /* add sdpplin version */
    SafeSprintf(psz256, 256, "a=SdpplinVersion:%u%s", m_ulVersion, pszEOL); 
    mDesc += psz256;
    
    mDesc += headerAttributes;

    // expand SDPData if it's there...
    if (pszSDPData)
    {
        mDesc += pszSDPData;
        HX_VECTOR_DELETE(pszSDPData);
        pszSDPData = NULL;
    }

    for(UINT16 i=2;i<nValues;++i)
    {
        if(pValueArray[i])
        {
            CHXString streamAttributes;
            streamAttributes.SetMinBufSize(8192);

            UINT32 streamNumber = 0;
            BOOL   bStreamNumberFound=FALSE;
            UINT32 duration = 0;
            BOOL   bDurationFound = FALSE;
            UINT32 rtpPayloadType = 0;
            BOOL   bRtpPayloadTypeFound=FALSE;
            UINT32 samplesPerSecond = 0;
            BOOL   bSamplesPerSecondFound=FALSE;
            UINT32 nChannels = 0;
            BOOL   bChannelsFound=FALSE;
            UINT32 port = 0;
            UINT32 ulPayloadType = 0;
            BOOL   bFoundRTCPRR = FALSE;
            UINT32 ulRTCPRR = 0;
            BOOL   bFoundRTCPSR = FALSE;
            UINT32 ulRTCPSR = 0;
            BOOL   bFoundPreDecBufSize = FALSE;
            UINT32 ulPreDecBufSize = 0;
            BOOL   bFoundPreDecBufPeriod = FALSE;
            UINT32 ulPreDecBufPeriod = 0;
            BOOL   bFoundPostDecBufPeriod = FALSE;
            UINT32 ulPostDecBufPeriod = 0;
            BOOL   bFoundDecByteRate = FALSE;
            UINT32 ulDecByteRate = 0;

            char* pszMimeType=NULL;
            char* pszMimeFirst=NULL;
            UINT32 ulMimeFirstBufLen = 0;
            char* pszMimeLast=NULL;
            
            UINT32 ptime=0;
            BOOL bPtimeFound=FALSE;
            char* pszFmtp=NULL;

            // Reuse connection strings from file header
            if (pszConnAddr)
            {
                HX_VECTOR_DELETE(pszConnAddr);
                pszConnAddr = NULL;
            }

            bFoundTTL = FALSE;
            bFoundRange = FALSE;
            port = 0;
            
            rc = pValueArray[i]->GetFirstPropertyULONG32(pPropName, propValue);
            while(rc == HXR_OK)
            {
                bAddToHeader = FALSE;

                if(strcasecmp(pPropName, "StreamNumber") == 0)
                {
                    streamNumber = propValue;
                    bStreamNumberFound = TRUE;
                }
                else if(strcasecmp(pPropName, "Duration") == 0)
                {
                    // don't use if 0 ( XXXJC we used to assert if the 
                    // prop was not present).
                    if (0 != propValue)
                    {
                        duration = propValue;
                        bDurationFound = TRUE;
                    }
                }
                else if(strcasecmp(pPropName, "RTPPayloadType") == 0)
                {
                    rtpPayloadType = propValue;
                    bRtpPayloadTypeFound = TRUE;
                }
                else if(strcasecmp(pPropName, "SamplesPerSecond") == 0)
                {
                    samplesPerSecond = propValue;
                    bSamplesPerSecondFound = TRUE;
                }
                else if(strcasecmp(pPropName, "Channels") == 0)
                {
                    nChannels = propValue;
                    bChannelsFound = TRUE;
                }
                else if(strcasecmp(pPropName, "MulticastTTL") == 0)
                {
                    connTTL = propValue;
                    bFoundTTL = TRUE;
                }
                else if(strcasecmp(pPropName, "MulticastRange") == 0)
                {
                    connRange = propValue;
                    bFoundRange = TRUE;
                }
                else if(strcasecmp(pPropName, "Port") == 0)
                {
                    port = propValue;
                }
                else if(strcasecmp(pPropName, "ptime") == 0)
                {
                    ptime = propValue;
                    bPtimeFound = TRUE;
                }   
                else if(strcasecmp(pPropName, "AvgBitRate") == 0)
                {
                    avgBitRate = propValue;
                    
                    // we need this in kbps
                    avgBitRate = (UINT32)((double)avgBitRate / 1000.0 + 0.5);
                    bFoundAvgBitRate = TRUE;
                    // add it to attribute as well.         
                    bAddToHeader = TRUE;    
                }
                else if(strcasecmp(pPropName, "RtcpRRRate") == 0)
                {
                    bFoundRTCPRR = TRUE;
                    ulRTCPRR = propValue;
                }
                else if(strcasecmp(pPropName, "RtcpRSRate") == 0)
                {
                    bFoundRTCPSR = TRUE;
                    ulRTCPSR = propValue;
                }
                else if(strcasecmp(pPropName, "PreDecBufSize") == 0)
                {
                    bFoundPreDecBufSize = TRUE;
                    ulPreDecBufSize = propValue;
                }
                else if(strcasecmp(pPropName, "InitPreDecBufPeriod") == 0)
                {
                    bFoundPreDecBufPeriod = TRUE;
                    ulPreDecBufPeriod = propValue;
                }
                else if(strcasecmp(pPropName, "InitPostDecBufPeriod") == 0)
                {
                    bFoundPostDecBufPeriod = TRUE;
                    ulPostDecBufPeriod = propValue;
                }
                else if(strcasecmp(pPropName, "DecByteRate") == 0)
                {
                    bFoundDecByteRate = TRUE;
                    ulDecByteRate = propValue;
                }

                else
                {
                    bAddToHeader = TRUE;                    
                }
        

                if (bAddToHeader)
                {
                    NEW_FAST_TEMP_STR(pszAttBuf, 4096, strlen(pPropName) + 64);
                    SafeSprintf(pszAttBuf, 4096, "a=%s:integer;%d%s", pPropName, 
                                                            propValue, pszEOL);
                    streamAttributes += pszAttBuf;
                    DELETE_FAST_TEMP_STR(pszAttBuf);
                }
                
                /* make sure to reset SdpFileType header */
                if (strncasecmp(pPropName, "SdpFileType", 11) == 0)
                {
                    pValueArray[i]->SetPropertyULONG32(pPropName, NONE_SDP);
                }
                rc = pValueArray[i]->GetNextPropertyULONG32(pPropName,
                    propValue);
            }
            rc = pValueArray[i]->GetFirstPropertyBuffer(pPropName, pPropBuffer);
            while(rc == HXR_OK)
            {
                INT32 dataSize = pPropBuffer->GetSize();

                NEW_FAST_TEMP_STR(pszAttBuf, 4096, dataSize * 2 +
                                                   strlen(pPropName) + 64);
                NEW_FAST_TEMP_STR(pszPropString, 4096, dataSize * 2 + 64);

                (void)BinTo64((const BYTE*)pPropBuffer->GetBuffer(),
                    dataSize, pszPropString);

                SafeSprintf(pszAttBuf, 4096, "a=%s:buffer;\"%s\"%s", pPropName, 
                                                      pszPropString, pszEOL);

                streamAttributes += pszAttBuf;
                DELETE_FAST_TEMP_STR(pszPropString);
                DELETE_FAST_TEMP_STR(pszAttBuf);

                HX_RELEASE(pPropBuffer);

                rc = pValueArray[i]->GetNextPropertyBuffer(pPropName,
                    pPropBuffer);
            }

            rc = pValueArray[i]->GetFirstPropertyCString(pPropName,
                pPropBuffer);
            while(rc == HXR_OK)
            {
                char* pString = (char*) pPropBuffer->GetBuffer();
                char* pszData=NULL;
                BOOL bDeleteString = FALSE;
                int len = pPropBuffer->GetSize();

                bAddToHeader = FALSE;

                if(strcasecmp(pPropName, "MimeType") == 0)
                {
                    pszMimeType = EscapeBuffer(pString, pPropBuffer->GetSize());

                    pszMimeLast = strchr(pszMimeType, '/');
                    if(pszMimeLast)
                    {
                        int MFLen = pszMimeLast-pszMimeType;
                        pszMimeLast++;
                        pszMimeFirst = new char [ MFLen+1 ];
                        ulMimeFirstBufLen = MFLen + 1;
                        memcpy(pszMimeFirst, pszMimeType, MFLen); /* Flawfinder: ignore */
                        pszMimeFirst[MFLen] = '\0';
                    }
                    else
                    {
                        // mimetype is not in the right format...
                    }
                }
                else if(strcasecmp(pPropName, "MulticastAddress") == 0)
                {
                    pszConnAddr = EscapeBuffer(pString, pPropBuffer->GetSize());
                }
                else if(strcasecmp(pPropName, "Control") == 0)
                {
                    if (pControlStringBuff)
                    {
                        HX_RELEASE(pControlStringBuff);
                        pControlStringBuff = pPropBuffer;
                        pControlStringBuff->AddRef(); 
                    }
                }
                else if(strcasecmp(pPropName, "PayloadParameters") == 0)
                {
                    pszFmtp = EscapeBuffer(pString, pPropBuffer->GetSize());
                }               
                else if(strcasecmp(pPropName, "SDPData") == 0)
                {
                    pszSDPData = new char [ len + 1 ];
                    memcpy(pszSDPData, (const char*)pPropBuffer->GetBuffer(), len); /* Flawfinder: ignore */
                    pszSDPData[len] = '\0';
		    RemoveASLine(pszSDPData, len);
                }
                else if(strcasecmp(pPropName, "Information") == 0)
                {
                    // "i="
                    HX_ASSERT(!pszInfo);
                    pszInfo = EscapeBuffer(pString, pPropBuffer->GetSize());
                }
                else
                {
                    pszData = EscapeBuffer(pString, pPropBuffer->GetSize());
                    bDeleteString = TRUE;
                    bAddToHeader = TRUE;                
                }
                
                if(bAddToHeader)
                {
                    NEW_FAST_TEMP_STR(pszAttBuf, 4096, strlen(pszData) +
                                                       strlen(pPropName) + 64);
                    SafeSprintf(pszAttBuf, 4096,"a=%s:string;\"%s\"%s", pPropName, 
                                                              pszData, pszEOL);
                    streamAttributes += pszAttBuf;
                    DELETE_FAST_TEMP_STR(pszAttBuf);
                }

                if (bDeleteString)
                {
                    HX_VECTOR_DELETE(pszData);
                }

                HX_RELEASE(pPropBuffer);

                rc = pValueArray[i]->GetNextPropertyCString(pPropName,
                                                            pPropBuffer);
            }

            if(!bRtpPayloadTypeFound)
            {
                ulPayloadType = RTP_PAYLOAD_RTSP;
            }
            else
            {
                ulPayloadType = rtpPayloadType;
            }

            if (bUseOldSdp)
            {   
                // our old sdpplin expects m= to have either "audio", 
                // "video", or "data", and anything else would be BAD!
                if (strcmp(pszMimeFirst, "audio") && strcmp(pszMimeFirst, "video"))
                {
                    SafeStrCpy(pszMimeFirst, "data", ulMimeFirstBufLen);
                }
            }
            else if (!pszMimeFirst)
            {
                pszMimeFirst = new char [ 5 ];
                strcpy(pszMimeFirst, "data"); /* Flawfinder: ignore */
            }

            // m=
            NEW_FAST_TEMP_STR(pszTmpString, 256, strlen(pszMimeFirst) + 128);
            SafeSprintf(pszTmpString, 256, "m=%s %d RTP/AVP %d%s", 
                pszMimeFirst, port, ulPayloadType, pszEOL);
            mDesc += pszTmpString;
            DELETE_FAST_TEMP_STR(pszTmpString);

            // i= 
            if (pszInfo)
            {
                NEW_FAST_TEMP_STR(pszTmpStr, 256, strlen(pszInfo) + 64);
                SafeSprintf(pszTmpStr, 256,"i=%s%s", pszInfo, pszEOL);
                mDesc += pszTmpStr;
                DELETE_FAST_TEMP_STR(pszTmpStr);

                HX_VECTOR_DELETE(pszInfo);
                pszInfo = NULL;
            }

            /* Format the connection line only if MulticastAddress and 
             * MulticastTTL exist in the stream header
             */
            if (pszConnAddr && strlen(pszConnAddr) && bFoundTTL)
            {
                NEW_FAST_TEMP_STR(pszTmpString, 256, strlen(pszConnAddr) + 128);
                if (bFoundRange)
                {
                    SafeSprintf(pszTmpString, 256,"c=IN IP4 %s/%d/%d%s", 
                        pszConnAddr, connTTL, connRange, pszEOL);
                }
                else
                {
                    SafeSprintf(pszTmpString, 256,"c=IN IP4 %s/%d%s", 
                        pszConnAddr, connTTL, pszEOL);
                }

                mDesc += pszTmpString;

                DELETE_FAST_TEMP_STR(pszTmpString);
            }

            // b=
            if (bFoundAvgBitRate)
            {
                SafeSprintf(psz256, 256,"b=AS:%u%s", avgBitRate, pszEOL); 
                mDesc += psz256;
                bFoundAvgBitRate = FALSE;
            }

            if (bFoundRTCPRR)
            {
                SafeSprintf(psz256, 256,"b=RR:%u%s", ulRTCPRR, pszEOL); 
                mDesc += psz256;
                bFoundRTCPRR = FALSE;
            }
            
            if (bFoundRTCPSR)
            {
                SafeSprintf(psz256, 256,"b=RS:%u%s", ulRTCPSR, pszEOL); 
                mDesc += psz256;
                bFoundRTCPSR = FALSE;
            }
                    
            // a=control
            if (pControlStringBuff)
            {
                char* pString = (char*) pControlStringBuff->GetBuffer();
                char* pszData=NULL;

                pszData = EscapeBuffer(pString, pControlStringBuff->GetSize());

                if (pContentBase || pQueryParams)
                {
                    UINT32 ulBuffLen = strlen(pszData);
                    ulBuffLen += (pContentBase) ? pContentBase->GetSize() : 0;
                    ulBuffLen += (pQueryParams) ? pQueryParams->GetSize() : 0;
                    ulBuffLen +=64;

                    NEW_FAST_TEMP_STR(pszControlString, 512, ulBuffLen); 
                    SafeSprintf(pszControlString, 512, "a=control:%s%s%s%s",
                    (pContentBase)? ((char*)pContentBase->GetBuffer()) : "",
                    pControlStringBuff->GetBuffer(),
                    (pQueryParams)?((char*)pQueryParams->GetBuffer()) : "",
                    pszEOL);
                    
                    mDesc += pszControlString;
                    DELETE_FAST_TEMP_STR(pszControlString);
                }
                else 
                {
                    UINT32 ulBuffLen = strlen(pszData) + 64;
                    NEW_FAST_TEMP_STR(pszControlString, 512, ulBuffLen); 
                    SafeSprintf(pszControlString,512,"a=control:%s%s", pszData,
                            pszEOL);
                
                    mDesc += pszControlString;
                    DELETE_FAST_TEMP_STR(pszControlString);
                }

                HX_VECTOR_DELETE(pszData);
            }
            else if (bStreamNumberFound)
            {
                if (pContentBase || pQueryParams)
                {
                    const char* pszFieldPrefix = "a=control:";
                    const char* pszStreamLabel = "streamid=";

                    UINT32 ulBuffLen = strlen(pszFieldPrefix) + 
                                       strlen(pszStreamLabel);
                    ulBuffLen += (pContentBase) ? (pContentBase->GetSize()) : 0;
                    ulBuffLen += (pQueryParams) ? (pQueryParams->GetSize()) : 0;
                    ulBuffLen += 1;

                    NEW_FAST_TEMP_STR(pszControlField, 256, ulBuffLen);
                    SafeSprintf(pszControlField, 256,"%s%s%s%d%s%s", 
                            pszFieldPrefix, 
                            (pContentBase)? ((char*)pContentBase->GetBuffer()) : "",
                            pszStreamLabel, streamNumber, 
                            (pQueryParams)? ((char*)pQueryParams->GetBuffer()) : "",
                            pszEOL);

                    mDesc += pszControlField;
                    DELETE_FAST_TEMP_STR(pszControlField);
                }
                else
                {
                    SafeSprintf(psz256, 256,"a=control:streamid=%d%s", streamNumber, pszEOL); 
                    mDesc += psz256;
                }
            }

            // a=length && a=range
            if (bDurationFound)
            {
                NPTime npTime(duration);

                if (!bIsLive)
                {
                    SafeSprintf(psz256,256, "a=range:npt=0-%s%s", (const char*)npTime, pszEOL);
                    mDesc += psz256;
                }
                
                SafeSprintf(psz256, 256, "a=length:npt=%s%s", (const char*)npTime, pszEOL);
                mDesc += psz256;
            }
            else if (bDefaultDurationFound)
            {
                // take it from file header
                NPTime npTime(ulDefaultDuration);

                if (!bIsLive)
                {
                    SafeSprintf(psz256, 256,"a=range:npt=0-%s%s", (const char*)npTime, pszEOL);
                    mDesc += psz256;
                }                   
                SafeSprintf(psz256,256, "a=length:npt=%s%s", (const char*)npTime,pszEOL);
                mDesc += psz256;
            }
            else
            {
                // if we put ntp=0-, old sdpplin is gonna freak out.
                if (bUseOldSdp)
                {
                    if (!bIsLive)
                    {
                        SafeSprintf(psz256, 256,"a=range:npt=0-0%s", pszEOL); 
                        mDesc += psz256;            
                    }
                    SafeSprintf(psz256, 256,"a=length:npt=0%s", pszEOL); 
                    mDesc += psz256;        
                }
                else
                {
                    if (!bIsLive)
                    {
                        SafeSprintf(psz256, 256,"a=range:npt=0-%s", pszEOL); 
                        mDesc += psz256;            
                    }                   
                    SafeSprintf(psz256, 256,"a=length:npt=0%s", pszEOL);
                    mDesc += psz256;        
                }
            }
            
            // a=rtpmap:
            if (!pszMimeLast)
            {
                goto bail;
            }
            
            SafeSprintf(psz256, 256, "a=rtpmap:%d %s", ulPayloadType, pszMimeLast);
            mDesc += psz256;

            if (SDPIsStaticPayload(ulPayloadType))
            {
                if (!bSamplesPerSecondFound)
                {
                    samplesPerSecond = 
                        SDPMapPayloadToSamplesPerSecond(ulPayloadType);
                    if (samplesPerSecond)
                    {
                        bSamplesPerSecondFound = TRUE;
                    }
                }
                if (!bChannelsFound)
                {
                    nChannels = SDPMapPayloadToChannels(ulPayloadType);
                    if (nChannels)
                    {
                        bChannelsFound = TRUE;
                    }
                }
            }
            if (bSamplesPerSecondFound)
            {
                SafeSprintf(psz256, 256,"/%d", samplesPerSecond); 
                mDesc += psz256;
            }
            else
            {
                mDesc += "/1000";
            }

            if (bChannelsFound)
            {
                SafeSprintf(psz256, 256,"/%d", nChannels); 
                mDesc += psz256;
            }
            mDesc += pszEOL;
            
            // a=fmtp
            if (pszFmtp)
            {
                SafeSprintf(psz256, 256, "a=fmtp:%d %s%s", ulPayloadType, pszFmtp, pszEOL);
                mDesc += psz256;

                HX_VECTOR_DELETE(pszFmtp);
            }
            else if (!bUseOldSdp)
            {   
                // 3GPP requires an fmtp field in every SDP file
                // Include this only if the client supports interop SDP
                // and SDPData doesn't already contain a=fmtp:
                SafeSprintf(psz256, 256,"a=fmtp:%d ", ulPayloadType); 
                if (!pszSDPData || !strstr(pszSDPData, psz256))
                {
                    SafeSprintf(psz256,256, "a=fmtp:%d %s", ulPayloadType, pszEOL); 
                    mDesc += psz256;
                }
            }

            // a=ptime
            if (bPtimeFound)
            {
                SafeSprintf(psz256,256, "a=ptime:%d%s", ptime, pszEOL);
                mDesc += psz256;
            }

            if (pszMimeType)
            {
                // a=mimetype:string;xxx
                NEW_FAST_TEMP_STR(pszTmpStr, 256, strlen(pszMimeType) + 128);
                SafeSprintf(pszTmpString, 256, "a=mimetype:string;\"%s\"%s", pszMimeType, pszEOL);
                mDesc += pszTmpString;
                DELETE_FAST_TEMP_STR(pszTmpStr);
                
                HX_VECTOR_DELETE(pszMimeType);
            }

            // 3GPP AnnexG values
            if (bFoundPreDecBufSize)
            {
                SafeSprintf(psz256, 256, "a=X-predecbufsize:%u%s", 
                    ulPreDecBufSize, pszEOL);
                mDesc += psz256;
            }
            if (bFoundPreDecBufPeriod)
            {
                SafeSprintf(psz256, 256, "a=X-initpredecbufperiod:%u%s", 
                    ulPreDecBufPeriod, pszEOL);
                mDesc += psz256;
            }
            if (bFoundPostDecBufPeriod)
            {
                SafeSprintf(psz256, 256, "a=X-initpostdecbufperiod:%u%s", 
                    ulPostDecBufPeriod, pszEOL);
                mDesc += psz256;
            }
            if (bFoundDecByteRate)
            {
                SafeSprintf(psz256, 256, "a=X-decbyterate:%u%s", 
                    ulDecByteRate, pszEOL);
                mDesc += psz256;
            }

            if (pszMimeFirst)
            {
                HX_VECTOR_DELETE(pszMimeFirst);
            }

            mDesc += streamAttributes;
            // expand SDPData if it's there...
            if (pszSDPData)
            {
                mDesc += pszSDPData;
                HX_VECTOR_DELETE(pszSDPData);
                pszSDPData = NULL;
            }           
        }
    }

bail:

    if (pszConnAddr)
        HX_VECTOR_DELETE(pszConnAddr);

    HX_RELEASE(pControlStringBuff);

    HX_RELEASE(pContentBase);
    HX_RELEASE(pQueryParams);

    if (mDesc.IsEmpty())
    {
        pDescription = NULL;
        return HXR_FAIL;
    }

    IHXBuffer* pBuffer = NULL;
    m_pCCF->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);
    pBuffer->Set((BYTE*)(const char*)mDesc, mDesc.GetLength());

    pDescription = pBuffer;

    return HXR_OK;
}

BOOL SDPMediaDescGenerator::GetUseOldEOL() const
{
    return m_bUseOldEOL;
}

void SDPMediaDescGenerator::SetUseOldEOL(BOOL bUseOldEOL)
{
    m_bUseOldEOL = bUseOldEOL;
}

/*
 *  If there is no Duration in a file header and all streams have the same 
 *  duration, put it in a file header.
 *  If live, it is 0.
 */
HX_RESULT
SDPMediaDescGenerator::SpecComplianceCheck(UINT16 nValues, 
                                        IHXValues** ppValueArray)
{
    HX_RESULT theErr;
    UINT32    ulDuration;

    // sanity check
    if (nValues < 3 || !ppValueArray)
    {
        HX_ASSERT(!"unexpected headers");
        return HXR_UNEXPECTED;
    }
    
    theErr = ppValueArray[0]->GetPropertyULONG32("Duration", ulDuration);
    if (SUCCEEDED(theErr))
    {
        return HXR_OK;
    }
    
    UINT32 ulTmp = 0;
    ulDuration = 0;
    
    /* find out if this is a live session */
    theErr = ppValueArray[0]->GetPropertyULONG32("LiveStream", ulTmp);
    if (SUCCEEDED(theErr) && 1 == ulTmp) 
    {
            ppValueArray[0]->SetPropertyULONG32("Duration", 0);
            return HXR_OK;
    }
    
    for (UINT16 i = 2; i < nValues; ++i)
    {
            theErr = 
                ppValueArray[i]->GetPropertyULONG32("Duration", ulTmp);
            if (SUCCEEDED(theErr))
            {                
	    // take the largest
	    if (ulDuration < ulTmp)
                {
		ulDuration = ulTmp;		
                }
            }
    }
    
    if (ulDuration)
    {
            ppValueArray[0]->SetPropertyULONG32("Duration", ulDuration);        
    }            

    return HXR_OK;
}

char*
SDPMediaDescGenerator::EscapeBuffer(const char* pBuffer, UINT32 len)
{
    UCHAR* buf;
    UINT32 newlen;
    UINT32 i = 0;
    UCHAR* tmp;
    UCHAR* newbuf;

    if(!pBuffer)
    {
        return 0;
    }

    buf = (UCHAR*)pBuffer;
    newlen = len;

    /*
     *  We have to do this in two passes.  One to alloc new mem to 
     *  copy into, and the next to do the escape/ copy.
     *  First, count unescaped quotes and alloc.
     */
    tmp = buf;
    while(i < len)
    {
        if(*tmp == '\"')
        {
            newlen++;
        }
        if(*tmp == '\\')
        {
            tmp++;
            i++;
        }
        tmp++;
        i++;
    }

    newbuf = new UCHAR[newlen];
    tmp = newbuf;

    /*
     * Now copy into the new buffer and unescape as you go along.
     */
    i = 0;
    while(i < len)
    {
        if(*buf == '\"')
        {
            *tmp++ = '\\';
        }
        if(*buf == '\\')
        {
            *tmp++ = *buf++;
            i++;
        }
        *tmp++ = *buf++;
        i++;
    }

    /*
     * Set the buffer and return.
     */
    return (char*)newbuf;
}

void
SDPMediaDescGenerator::RemoveASLine(char* pSDPData, UINT32 len)
{
    /* 
     * This method removes an extraneous b=AS line in place.
     */

    if (!pSDPData)
    {
	return;
    }

    char* pWriteCursor = (char*)strstr((const char*)pSDPData, "b=AS");

    if (!pWriteCursor)
    {
	return;
    }

    char* pReadCursor = pWriteCursor;
    UINT32 ulLength = (len - (pWriteCursor - pSDPData));

    //Find the end of the field
    while((ulLength != 0) &&
	  (*pReadCursor != 0x0a) &&
	  (*pReadCursor != 0x0d) &&
          (*pReadCursor != ' '))
    {
        pReadCursor++;
        ulLength--;
    }

    //Skip over the end of the field
    while((ulLength > 0) &&
          ((*pReadCursor == 0x0a) ||
           (*pReadCursor == 0x0d) ||
           (*pReadCursor == ' ')))
    {
        pReadCursor++;
        ulLength--;
    } 

    if (!pReadCursor)
    {
	return;
    }

    if (ulLength == 0)
    {
	/*
	 * There's no more data beyond the b=AS line.
	 * Terminate the SDPData after previous line
	 */
	*pWriteCursor = '\0';
    }
    else
    {
	memmove(pWriteCursor, pReadCursor, len - (pReadCursor - pSDPData));
    }
}

BOOL SDPMediaDescGenerator::GetUseSessionGUID() const
{
    return m_bUseSessionGUID;
}

BOOL SDPMediaDescGenerator::GetUseAbsoluteURL() const
{
    return m_bUseAbsoluteURL;
}

void SDPMediaDescGenerator::SetUseSessionGUID(BOOL bOption)
{
    m_bUseSessionGUID = bOption;
}

void SDPMediaDescGenerator::SetUseAbsoluteURL(BOOL bOption)
{
    m_bUseAbsoluteURL= bOption;
}
