/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2008 Apple Inc.  All Rights Reserved.
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 */
/*
    File:       RTPSessionInterface.h

    Contains:   Implementation of object defined in .h
                


    

*/

#include "RTPSessionInterface.h"
#include "QTSServerInterface.h"
#include "RTSPRequestInterface.h"
#include "QTSS.h"
#include "OS.h"
#include "RTPStream.h"
#include "md5.h"
#include "md5digest.h"
#include "base64.h"

unsigned int            RTPSessionInterface::sRTPSessionIDCounter = 0;


QTSSAttrInfoDict::AttrInfo  RTPSessionInterface::sAttributes[] = 
{   /*fields:   fAttrName, fFuncPtr, fAttrDataType, fAttrPermission */
    /* 0  */ { "qtssCliSesStreamObjects",           NULL,   qtssAttrDataTypeQTSS_Object,    qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 1  */ { "qtssCliSesCreateTimeInMsec",        NULL,   qtssAttrDataTypeTimeVal,        qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 2  */ { "qtssCliSesFirstPlayTimeInMsec",     NULL,   qtssAttrDataTypeTimeVal,        qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 3  */ { "qtssCliSesPlayTimeInMsec",          NULL,   qtssAttrDataTypeTimeVal,        qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 4  */ { "qtssCliSesAdjustedPlayTimeInMsec",  NULL,   qtssAttrDataTypeTimeVal,        qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 5  */ { "qtssCliSesRTPBytesSent",            NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 6  */ { "qtssCliSesRTPPacketsSent",          NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 7  */ { "qtssCliSesState",                   NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 8  */ { "qtssCliSesPresentationURL",         NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 9  */ { "qtssCliSesFirstUserAgent",          NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 10 */ { "qtssCliStrMovieDurationInSecs",     NULL,   qtssAttrDataTypeFloat64,        qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
    /* 11 */ { "qtssCliStrMovieSizeInBytes",        NULL,   qtssAttrDataTypeUInt64,         qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
    /* 12 */ { "qtssCliSesMovieAverageBitRate",     NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
    /* 13 */ { "qtssCliSesLastRTSPSession",         NULL,   qtssAttrDataTypeQTSS_Object,    qtssAttrModeRead | qtssAttrModePreempSafe } ,
    /* 14 */ { "qtssCliSesFullURL",                 NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe } ,
    /* 15 */ { "qtssCliSesHostName",                NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },

    /* 16 */ { "qtssCliRTSPSessRemoteAddrStr",      NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 17 */ { "qtssCliRTSPSessLocalDNS",           NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 18 */ { "qtssCliRTSPSessLocalAddrStr",       NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 19 */ { "qtssCliRTSPSesUserName",            NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 20 */ { "qtssCliRTSPSesUserPassword",        NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 21 */ { "qtssCliRTSPSesURLRealm",            NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 22 */ { "qtssCliRTSPReqRealStatusCode",      NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 23 */ { "qtssCliTeardownReason",             NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe | qtssAttrModeWrite },
    /* 24 */ { "qtssCliSesReqQueryString",          NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 25 */ { "qtssCliRTSPReqRespMsg",             NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    
    /* 26 */ { "qtssCliSesCurrentBitRate",          CurrentBitRate,     qtssAttrDataTypeUInt32,  qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 27 */ { "qtssCliSesPacketLossPercent",       PacketLossPercent,  qtssAttrDataTypeFloat32, qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 28 */ { "qtssCliSesTimeConnectedinMsec",     TimeConnected,      qtssAttrDataTypeSInt64,  qtssAttrModeRead | qtssAttrModePreempSafe },    
    /* 29 */ { "qtssCliSesCounterID",               NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 30 */ { "qtssCliSesRTSPSessionID",           NULL,   qtssAttrDataTypeCharArray,      qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 31 */ { "qtssCliSesFramesSkipped",           NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe },
	/* 32 */ { "qtssCliSesTimeoutMsec", 			NULL, 	qtssAttrDataTypeUInt32,		qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe },
	/* 33 */ { "qtssCliSesOverBufferEnabled",       NULL, 	qtssAttrDataTypeBool16,		qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe },
    /* 34 */ { "qtssCliSesRTCPPacketsRecv",         NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe },
    /* 35 */ { "qtssCliSesRTCPBytesRecv",           NULL,   qtssAttrDataTypeUInt32,         qtssAttrModeRead | qtssAttrModePreempSafe },
	/* 36 */ { "qtssCliSesStartedThinning",         NULL, 	qtssAttrDataTypeBool16,		qtssAttrModeRead | qtssAttrModeWrite  | qtssAttrModePreempSafe },
	/* 37 */ { "qtssCliSes3GPPObject",              NULL, 	qtssAttrDataTypeQTSS_Object,		qtssAttrModeRead | qtssAttrModePreempSafe },
	/* 38 */ { "qtssCliSessLastRTSPBandwidth",      NULL, 	qtssAttrDataTypeUInt32,		qtssAttrModeRead | qtssAttrModePreempSafe },
	/* 39 */ { "qtssCliSessIs3GPPSession",          NULL, 	qtssAttrDataTypeBool16,		qtssAttrModeRead | qtssAttrModePreempSafe }
	
    
};

void    RTPSessionInterface::Initialize()
{
    for (UInt32 x = 0; x < qtssCliSesNumParams; x++)
        QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kClientSessionDictIndex)->
            SetAttribute(x, sAttributes[x].fAttrName, sAttributes[x].fFuncPtr,
                sAttributes[x].fAttrDataType, sAttributes[x].fAttrPermission);
}

RTPSessionInterface::RTPSessionInterface()
:   QTSSDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kClientSessionDictIndex), NULL),
    Task(),
    fLastQualityCheckTime(0),
	fLastQualityCheckMediaTime(0),
	fStartedThinning(false),    
    fIsFirstPlay(true),
    fAllTracksInterleaved(true), // assume true until proven false!
    fFirstPlayTime(0),
    fPlayTime(0),
    fAdjustedPlayTime(0),
    fNTPPlayTime(0),
    fNextSendPacketsTime(0),
    fSessionQualityLevel(0),
    fState(qtssPausedState),
    fPlayFlags(0),
    fLastBitRateBytes(0),
    fLastBitRateUpdateTime(0),
    fMovieCurrentBitRate(0),
    fRTSPSession(NULL),
    fLastRTSPReqRealStatusCode(200),
    fTimeoutTask(NULL, QTSServerInterface::GetServer()->GetPrefs()->GetRTPTimeoutInSecs() * 1000),
    fNumQualityLevels(0),
    fBytesSent(0),
    fPacketsSent(0),
    fPacketLossPercent(0.0),
    fTimeConnected(0),
    fTotalRTCPPacketsRecv(0),    
    fTotalRTCPBytesRecv(0),    
    fMovieDuration(0),
    fMovieSizeInBytes(0),
    fMovieAverageBitRate(0),
    fTeardownReason(0),
    fUniqueID(0),
    fTracker(QTSServerInterface::GetServer()->GetPrefs()->IsSlowStartEnabled()),
	fOverbufferWindow(QTSServerInterface::GetServer()->GetPrefs()->GetSendIntervalInMsec(),kUInt32_Max, QTSServerInterface::GetServer()->GetPrefs()->GetMaxSendAheadTimeInSecs(),
QTSServerInterface::GetServer()->GetPrefs()->GetOverbufferRate()),
    fAuthScheme(QTSServerInterface::GetServer()->GetPrefs()->GetAuthScheme()),
    fAuthQop(RTSPSessionInterface::kNoQop),
    fAuthNonceCount(0),
    fFramesSkipped(0),
    fRTPSession3GPP(QTSServerInterface::GetServer()->GetPrefs()->Get3GPPEnabled() ),
    fRTPSession3GPPPtr(&fRTPSession3GPP),
    fLastRTSPBandwidthHeaderBits(0),
    fIs3GPPSession(false)
{
    //don't actually setup the fTimeoutTask until the session has been bound!
    //(we don't want to get timeouts before the session gets bound)

    fTimeoutTask.SetTask(this);
    fTimeout = QTSServerInterface::GetServer()->GetPrefs()->GetRTPTimeoutInSecs() * 1000;
    fUniqueID = (UInt32)atomic_add(&sRTPSessionIDCounter, 1);
    
    // fQualityUpdate is a counter the starting value is the unique ID so every session starts at a different position
    fQualityUpdate = fUniqueID;
    
    //mark the session create time
    fSessionCreateTime = OS::Milliseconds();

    // Setup all dictionary attribute values
    
    // Make sure the dictionary knows about our preallocated memory for the RTP stream array
    this->SetEmptyVal(qtssCliSesFirstUserAgent, &fUserAgentBuffer[0], kUserAgentBufSize);
    this->SetEmptyVal(qtssCliSesStreamObjects, &fStreamBuffer[0], kStreamBufSize);
    this->SetEmptyVal(qtssCliSesPresentationURL, &fPresentationURL[0], kPresentationURLSize);
    this->SetEmptyVal(qtssCliSesFullURL, &fFullRequestURL[0], kRequestHostNameBufferSize);
    this->SetEmptyVal(qtssCliSesHostName, &fRequestHostName[0], kFullRequestURLBufferSize);

    this->SetVal(qtssCliSesCreateTimeInMsec,    &fSessionCreateTime, sizeof(fSessionCreateTime));
    this->SetVal(qtssCliSesFirstPlayTimeInMsec, &fFirstPlayTime, sizeof(fFirstPlayTime));
    this->SetVal(qtssCliSesPlayTimeInMsec,      &fPlayTime, sizeof(fPlayTime));
    this->SetVal(qtssCliSesAdjustedPlayTimeInMsec, &fAdjustedPlayTime, sizeof(fAdjustedPlayTime));
    this->SetVal(qtssCliSesRTPBytesSent,        &fBytesSent, sizeof(fBytesSent));
    this->SetVal(qtssCliSesRTPPacketsSent,      &fPacketsSent, sizeof(fPacketsSent));
    this->SetVal(qtssCliSesState,               &fState, sizeof(fState));
    this->SetVal(qtssCliSesMovieDurationInSecs, &fMovieDuration, sizeof(fMovieDuration));
    this->SetVal(qtssCliSesMovieSizeInBytes,    &fMovieSizeInBytes, sizeof(fMovieSizeInBytes));
    this->SetVal(qtssCliSesLastRTSPSession,     &fRTSPSession, sizeof(fRTSPSession));
    this->SetVal(qtssCliSesMovieAverageBitRate, &fMovieAverageBitRate, sizeof(fMovieAverageBitRate));
    this->SetEmptyVal(qtssCliRTSPSessRemoteAddrStr, &fRTSPSessRemoteAddrStr[0], kIPAddrStrBufSize );
    this->SetEmptyVal(qtssCliRTSPSessLocalDNS, &fRTSPSessLocalDNS[0], kLocalDNSBufSize);
    this->SetEmptyVal(qtssCliRTSPSessLocalAddrStr, &fRTSPSessLocalAddrStr[0], kIPAddrStrBufSize);

    this->SetEmptyVal(qtssCliRTSPSesUserName, &fUserNameBuf[0],RTSPSessionInterface::kMaxUserNameLen);
    this->SetEmptyVal(qtssCliRTSPSesUserPassword, &fUserPasswordBuf[0], RTSPSessionInterface::kMaxUserPasswordLen);
    this->SetEmptyVal(qtssCliRTSPSesURLRealm, &fUserRealmBuf[0], RTSPSessionInterface::kMaxUserRealmLen);

    this->SetVal(qtssCliRTSPReqRealStatusCode, &fLastRTSPReqRealStatusCode, sizeof(fLastRTSPReqRealStatusCode));

    this->SetVal(qtssCliTeardownReason, &fTeardownReason, sizeof(fTeardownReason));
 //   this->SetVal(qtssCliSesCurrentBitRate, &fMovieCurrentBitRate, sizeof(fMovieCurrentBitRate));
    this->SetVal(qtssCliSesCounterID, &fUniqueID, sizeof(fUniqueID));
    this->SetEmptyVal(qtssCliSesRTSPSessionID, &fRTSPSessionIDBuf[0], QTSS_MAX_SESSION_ID_LENGTH + 4);
    this->SetVal(qtssCliSesFramesSkipped, &fFramesSkipped, sizeof(fFramesSkipped));
    this->SetVal(qtssCliSesRTCPPacketsRecv, &fTotalRTCPPacketsRecv, sizeof(fTotalRTCPPacketsRecv));
    this->SetVal(qtssCliSesRTCPBytesRecv, &fTotalRTCPBytesRecv, sizeof(fTotalRTCPBytesRecv));

	this->SetVal(qtssCliSesTimeoutMsec,	&fTimeout, sizeof(fTimeout));
	
	this->SetVal(qtssCliSesOverBufferEnabled, this->GetOverbufferWindow()->OverbufferingEnabledPtr(), sizeof(Bool16));
	this->SetVal(qtssCliSesStartedThinning, &fStartedThinning, sizeof(Bool16));
	this->SetVal(qtssCliSes3GPPObject, &fRTPSession3GPPPtr, sizeof(fRTPSession3GPPPtr));
	
    this->SetVal(qtssCliSessLastRTSPBandwidth, &fLastRTSPBandwidthHeaderBits, sizeof(fLastRTSPBandwidthHeaderBits));
    this->SetVal(qtssCliSessIs3GPPSession, &fIs3GPPSession, sizeof(fIs3GPPSession));
    
    
}

void RTPSessionInterface::SetValueComplete(UInt32 inAttrIndex, QTSSDictionaryMap* inMap,
							UInt32 inValueIndex, void* inNewValue, UInt32 inNewValueLen)
{
	if (inAttrIndex == qtssCliSesTimeoutMsec)
	{
		Assert(inNewValueLen == sizeof(UInt32));
		UInt32 newTimeOut = *((UInt32 *) inNewValue);
		fTimeoutTask.SetTimeout((SInt64) newTimeOut);
	}
}

void RTPSessionInterface::UpdateRTSPSession(RTSPSessionInterface* inNewRTSPSession)
{   
    if (inNewRTSPSession != fRTSPSession)
    {
        // If there was an old session, let it know that we are done
        if (fRTSPSession != NULL)
            fRTSPSession->DecrementObjectHolderCount();
        
        // Increment this count to prevent the RTSP session from being deleted
        fRTSPSession = inNewRTSPSession;
        fRTSPSession->IncrementObjectHolderCount();
    }
}

char* RTPSessionInterface::GetSRBuffer(UInt32 inSRLen)
{
    if (fSRBuffer.Len < inSRLen)
    {
        delete [] fSRBuffer.Ptr;
        fSRBuffer.Set(NEW char[2*inSRLen], 2*inSRLen);
    }
    return fSRBuffer.Ptr;
}

QTSS_Error RTPSessionInterface::DoSessionSetupResponse(RTSPRequestInterface* inRequest)
{
    // This function appends a session header to the SETUP response, and
    // checks to see if it is a 304 Not Modified. If it is, it sends the entire
    // response and returns an error
    if ( QTSServerInterface::GetServer()->GetPrefs()->GetRTSPTimeoutInSecs() > 0 )  // adv the timeout
        inRequest->AppendSessionHeaderWithTimeout( this->GetValue(qtssCliSesRTSPSessionID), QTSServerInterface::GetServer()->GetPrefs()->GetRTSPTimeoutAsString() );
    else
        inRequest->AppendSessionHeaderWithTimeout( this->GetValue(qtssCliSesRTSPSessionID), NULL ); // no timeout in resp.
    
    if (inRequest->GetStatus() == qtssRedirectNotModified)
    {
        (void)inRequest->SendHeader();
        return QTSS_RequestFailed;
    }
    return QTSS_NoErr;
}

void RTPSessionInterface::UpdateBitRateInternal(const SInt64& curTime)
{   
    if (fState == qtssPausedState)
    {    fMovieCurrentBitRate = 0;
         fLastBitRateUpdateTime = curTime;
         fLastBitRateBytes = fBytesSent;
    }
    else
    {
        UInt32 bitsInInterval = (fBytesSent - fLastBitRateBytes) * 8;
        SInt64 updateTime = (curTime - fLastBitRateUpdateTime) / 1000;
        if (updateTime > 0) // leave Bit Rate the same if updateTime is 0 also don't divide by 0.
            fMovieCurrentBitRate = (UInt32) (bitsInInterval / updateTime);
        fTracker.UpdateAckTimeout(bitsInInterval, curTime - fLastBitRateUpdateTime);
        fLastBitRateBytes = fBytesSent;
        fLastBitRateUpdateTime = curTime;
    }
    //qtss_printf("fMovieCurrentBitRate=%"_U32BITARG_"\n",fMovieCurrentBitRate);
    //qtss_printf("Cur bandwidth: %d. Cur ack timeout: %d.\n",fTracker.GetCurrentBandwidthInBps(), fTracker.RecommendedClientAckTimeout());
}

void* RTPSessionInterface::TimeConnected(QTSSDictionary* inSession, UInt32* outLen)
{
    RTPSessionInterface* theSession = (RTPSessionInterface*)inSession;
    theSession->fTimeConnected = (OS::Milliseconds() - theSession->GetSessionCreateTime());

    // Return the result
    *outLen = sizeof(theSession->fTimeConnected);
    return &theSession->fTimeConnected;
}

void* RTPSessionInterface::CurrentBitRate(QTSSDictionary* inSession, UInt32* outLen)
{
    RTPSessionInterface* theSession = (RTPSessionInterface*)inSession;
    theSession->UpdateBitRateInternal(OS::Milliseconds());
    
    // Return the result
    *outLen = sizeof(theSession->fMovieCurrentBitRate);
    return &theSession->fMovieCurrentBitRate;
}


void* RTPSessionInterface::PacketLossPercent(QTSSDictionary* inSession, UInt32* outLen)
{   
    RTPSessionInterface* theSession = (RTPSessionInterface*)inSession;
    RTPStream* theStream = NULL;
    UInt32 theLen = sizeof(theStream);
            
    SInt64 packetsLost = 0;
    SInt64 packetsSent = 0;
    
    for (int x = 0; theSession->GetValue(qtssCliSesStreamObjects, x, (void*)&theStream, &theLen) == QTSS_NoErr; x++)
    {       
        if (theStream != NULL  )
        {
            UInt32 streamCurPacketsLost = 0;
            theLen = sizeof(UInt32);
            (void) theStream->GetValue(qtssRTPStrCurPacketsLostInRTCPInterval,0, &streamCurPacketsLost, &theLen);
            //qtss_printf("stream = %d streamCurPacketsLost = %"_U32BITARG_" \n",x, streamCurPacketsLost);
            
            UInt32 streamCurPackets = 0;
            theLen = sizeof(UInt32);
            (void) theStream->GetValue(qtssRTPStrPacketCountInRTCPInterval,0, &streamCurPackets, &theLen);
            //qtss_printf("stream = %d streamCurPackets = %"_U32BITARG_" \n",x, streamCurPackets);
                
            packetsSent += (SInt64)  streamCurPackets;
            packetsLost += (SInt64) streamCurPacketsLost;
            //qtss_printf("stream calculated loss = %f \n",x, (Float32) streamCurPacketsLost / (Float32) streamCurPackets);
            
        }

        theStream = NULL;
        theLen = sizeof(UInt32);
    }
    
    //Assert(packetsLost <= packetsSent);
    if (packetsSent > 0)
    {   if  (packetsLost <= packetsSent)
            theSession->fPacketLossPercent =(Float32) (( ((Float32) packetsLost / (Float32) packetsSent) * 100.0) );
        else
            theSession->fPacketLossPercent = 100.0;
    }
    else
        theSession->fPacketLossPercent = 0.0;
    //qtss_printf("Session loss percent packetsLost = %qd packetsSent= %qd theSession->fPacketLossPercent=%f\n",packetsLost,packetsSent,theSession->fPacketLossPercent);
    // Return the result
    *outLen = sizeof(theSession->fPacketLossPercent);
    return &theSession->fPacketLossPercent;
}

void RTPSessionInterface::CreateDigestAuthenticationNonce() {

    // Calculate nonce: MD5 of sessionid:timestamp
    SInt64 curTime = OS::Milliseconds();
    char* curTimeStr = NEW char[128];
    qtss_sprintf(curTimeStr, "%"_64BITARG_"d", curTime);
    
    // Delete old nonce before creating a new one
    if(fAuthNonce.Ptr != NULL)
        delete [] fAuthNonce.Ptr;
        
    MD5_CTX ctxt;
    unsigned char nonceStr[16];
    unsigned char colon[] = ":";
    MD5_Init(&ctxt);
    StrPtrLen* sesID = this->GetValue(qtssCliSesRTSPSessionID);
    MD5_Update(&ctxt, (unsigned char *)sesID->Ptr, sesID->Len);
    MD5_Update(&ctxt, (unsigned char *)colon, 1);
    MD5_Update(&ctxt, (unsigned char *)curTimeStr, ::strlen(curTimeStr));
    MD5_Final(nonceStr, &ctxt);
    HashToString(nonceStr, &fAuthNonce);

    delete [] curTimeStr; // No longer required once nonce is created
        
    // Set the nonce count value to zero 
    // as a new nonce has been created  
    fAuthNonceCount = 0;

}

void RTPSessionInterface::SetChallengeParams(QTSS_AuthScheme scheme, UInt32 qop, Bool16 newNonce, Bool16 createOpaque)
{   
    // Set challenge params 
    // Set authentication scheme
    fAuthScheme = scheme;   
    
    if(fAuthScheme == qtssAuthDigest) {
        // Set Quality of Protection 
        // auth-int (Authentication with integrity) not supported yet
        fAuthQop = qop;
    
        if(newNonce || (fAuthNonce.Ptr == NULL))
            this->CreateDigestAuthenticationNonce();
    
        if(createOpaque) {
            // Generate a random UInt32 and convert it to a string 
            // The base64 encoded form of the string is made the opaque value
            SInt64 theMicroseconds = OS::Microseconds();
            ::srand((unsigned int)theMicroseconds);
            UInt32 randomNum = ::rand();
            char* randomNumStr = NEW char[128];
            qtss_sprintf(randomNumStr, "%"_U32BITARG_"", randomNum);
            int len = ::strlen(randomNumStr);
            fAuthOpaque.Len = Base64encode_len(len);
            char *opaqueStr = NEW char[fAuthOpaque.Len];
            (void) Base64encode(opaqueStr, randomNumStr,len);
            delete [] randomNumStr;                 // Don't need this anymore
            if(fAuthOpaque.Ptr != NULL)             // Delete existing pointer before assigning new
                delete [] fAuthOpaque.Ptr;              // one
            fAuthOpaque.Ptr = opaqueStr;
        }
        else {
            if(fAuthOpaque.Ptr != NULL) 
                delete [] fAuthOpaque.Ptr;
            fAuthOpaque.Len = 0;    
        }
        // Increase the Nonce Count by one
        // This number is a count of the next request the server
        // expects with this nonce. (Implies that the server
        // has already received nonce count - 1 requests that 
        // sent authorization with this nonce
        fAuthNonceCount ++;
    }
}

void RTPSessionInterface::UpdateDigestAuthChallengeParams(Bool16 newNonce, Bool16 createOpaque, UInt32 qop) {
    if(newNonce || (fAuthNonce.Ptr == NULL))
        this->CreateDigestAuthenticationNonce();
    
            
    if(createOpaque) {
        // Generate a random UInt32 and convert it to a string 
        // The base64 encoded form of the string is made the opaque value
        SInt64 theMicroseconds = OS::Microseconds();
        ::srand((unsigned int)theMicroseconds);
        UInt32 randomNum = ::rand();
        char* randomNumStr = NEW char[128];
        qtss_sprintf(randomNumStr, "%"_U32BITARG_"", randomNum);
        int len = ::strlen(randomNumStr);
        fAuthOpaque.Len = Base64encode_len(len);
        char *opaqueStr = NEW char[fAuthOpaque.Len];
        (void) Base64encode(opaqueStr, randomNumStr,len);
        delete [] randomNumStr;                 // Don't need this anymore
        if(fAuthOpaque.Ptr != NULL)             // Delete existing pointer before assigning new
            delete [] fAuthOpaque.Ptr;              // one
        fAuthOpaque.Ptr = opaqueStr;
        fAuthOpaque.Len = ::strlen(opaqueStr);
    }
    else {
        if(fAuthOpaque.Ptr != NULL) 
            delete [] fAuthOpaque.Ptr;
        fAuthOpaque.Len = 0;    
    }
    fAuthNonceCount ++;
    
    fAuthQop = qop;
}
