/*----------------------------------------------------------------------------
 * File:        RTCPSSRC.C
 * Product:     RTP/RTCP implementation
 * Description: Provides SSRC related function.
 *
 * INTEL Corporation Proprietary Information
 * This listing is supplied under the terms of a license agreement with 
 * Intel Corporation and may not be copied nor disclosed except in 
 * accordance with the terms of that agreement.
 * Copyright (c) 1995 Intel Corporation. 
 *--------------------------------------------------------------------------*/


#include "rrcm.h"
#include "md5.h"



/*---------------------------------------------------------------------------
/							Global Variables
/--------------------------------------------------------------------------*/            


/*---------------------------------------------------------------------------
/							External Variables
/--------------------------------------------------------------------------*/                                       
extern PRTCP_CONTEXT	pRTCPContext;

#ifdef ENABLE_ISDM2
extern KEY_HANDLE		hRRCMRootKey;
extern ISDM2			Isdm2;
#endif

#ifdef _DEBUG
extern char		debug_string[];
#endif
    


/*----------------------------------------------------------------------------
 * Function   : getOneSSRCentry
 * Description: Get an SSRC entry from the free list of entries.
 * 
 * Input :      pList		: -> to the list to get the entry from
 *				hHeap		: Handle to the heap where the data resides
 *				*pNum		: -> to the number of initial free entry in the list
 *				*pCritSect	: -> to the critical section
 *
 * Return: 		OK:		-> to SSRC entry
 *				Error:	NULL
 ---------------------------------------------------------------------------*/
PSSRC_ENTRY getOneSSRCentry (PLINK_LIST pList, 
							 HANDLE hHeap, 
							 DWORD *pNum,
							 CRITICAL_SECTION *pCritSect)
	{     
	PSSRC_ENTRY	pSSRC = NULL;

	IN_OUT_STR ("RTCP: Enter getOneSSRCentry()\n");

	// get an entry from the free list
	pSSRC = (PSSRC_ENTRY)removePcktFromHead (pList, pCritSect);
	if (pSSRC == NULL)
		{
		// try to reallocate some free cells
		if (allocateLinkedList (pList, hHeap, pNum, 
								sizeof(SSRC_ENTRY),
								pCritSect) == RRCM_NoError)
			{		 						
			// get a free cell if some have been reallocated
			pSSRC = (PSSRC_ENTRY)removePcktFromHead (pList, pCritSect);
			}
		}

	if (pSSRC)
		{
		clearSSRCEntry (pSSRC);

		// initialize the critical section
		InitializeCriticalSection(&pSSRC->critSect);
		}

	IN_OUT_STR ("RTCP: Exit getOneSSRCentry()\n");
		
	return (pSSRC);
	}		                                                                                                                                                            
	
                                                                                  
/*----------------------------------------------------------------------------
 * Function   : getSSRC
 * Description: Get a unique 32 bits SSRC
 * 
 * Input :      RcvSSRCList: Session's receive SSRC list address
 *				XmtSSRCList: Session's transmit SSRC list address
 *
 * Return:      Unique 32 bits SSRC
 ---------------------------------------------------------------------------*/
 DWORD getSSRC (LINK_LIST RcvSSRCList, 
				LINK_LIST XmtSSRCList)
	{               
	DWORD		SSRCnum = 0;
	DWORD		dwStatus;
	PSSRC_ENTRY	pSSRC;
	MD5_CTX		context;
	DWORD		i;
	union {
		unsigned char	c[16];
		DWORD			x[4];
		}digest;

	struct {
		DWORD		pid;
		DWORD		time;
		FILETIME	createTime;
		FILETIME	exitTime;
		FILETIME	kernelTime;
		FILETIME	userTime;
		} md5Input;

	IN_OUT_STR ("RTCP: Enter getSSRC()\n");

	// go through all SSRCs of this RTP/RTCP session
	while (SSRCnum == 0)
		{
		// get MD5 inputs
		md5Input.pid  = GetCurrentThreadId();
		md5Input.time = timeGetTime();

		dwStatus = GetProcessTimes (GetCurrentProcess(),
									&md5Input.createTime, 
									&md5Input.exitTime, 
									&md5Input.kernelTime, 
									&md5Input.userTime);
		if (dwStatus == FALSE)
			{
			RRCM_DBG_MSG ("RTCP: GetProcessTimes() failed", GetLastError(),
						  __FILE__, __LINE__, DBG_NOTIFY);
			}

		// Implementation suggested by draft 08, Appendix 6
		MD5Init (&context);
		MD5Update (&context, (unsigned char *)&md5Input, sizeof (md5Input));
		MD5Final ((unsigned char *)&digest, &context);
		SSRCnum = 0;
		for (i=0; i < 3; i++)
			SSRCnum ^= digest.x[i];

		// look through all transmitter for this session
		pSSRC = (PSSRC_ENTRY)XmtSSRCList.prev;
		if (isSSRCunique (pSSRC, &SSRCnum) == TRUE)
			{
			// look through all received SSRC for this session
			pSSRC = (PSSRC_ENTRY)RcvSSRCList.prev;
			isSSRCunique (pSSRC, &SSRCnum);
			}
		}

	IN_OUT_STR ("RTCP: Exit getSSRC()\n");

	return (SSRCnum);
	}


 /*----------------------------------------------------------------------------
 * Function   : getAnSSRC
 * Description: Build an SSRC according to the RFC, but does not check for 
 *				collision. Mainly used by H.323 to get a 32 bits number.
 * 
 * Input :      None
 *
 * Return:      32 bits SSRC
 ---------------------------------------------------------------------------*/
  DWORD WINAPI getAnSSRC (void)
	{               
	DWORD		SSRCnum = 0;
	DWORD		dwStatus;
	MD5_CTX		context;
	DWORD		i;
	union {
		unsigned char	c[16];
		DWORD			x[4];
		}digest;

	struct {
		DWORD		pid;
		DWORD		time;
		FILETIME	createTime;
		FILETIME	exitTime;
		FILETIME	kernelTime;
		FILETIME	userTime;
		} md5Input;

	IN_OUT_STR ("RTCP: Enter getAnSSRC()\n");

	// get MD5 inputs
	md5Input.pid  = GetCurrentThreadId();
	md5Input.time = timeGetTime();

	dwStatus = GetProcessTimes (GetCurrentProcess(),
								&md5Input.createTime, 
								&md5Input.exitTime, 
								&md5Input.kernelTime, 
								&md5Input.userTime);
	if (dwStatus == FALSE)
		{
		RRCM_DBG_MSG ("RTCP: GetProcessTimes() failed", GetLastError(),
					  __FILE__, __LINE__, DBG_NOTIFY);
		}

	// Implementation suggested by draft 08, Appendix 6
	MD5Init (&context);
	MD5Update (&context, (unsigned char *)&md5Input, sizeof (md5Input));
	MD5Final ((unsigned char *)&digest, &context);
	SSRCnum = 0;
	for (i=0; i < 3; i++)
		SSRCnum ^= digest.x[i];

	IN_OUT_STR ("RTCP: Exit getAnSSRC()\n");

	return (SSRCnum);
	}


/*----------------------------------------------------------------------------
 * Function   : isSSRCunique
 * Description: Check to see the SSRC already exist
 * 
 * Input :      pSSRC		:	-> to an SSRC list
 *				*SSRCnum	:	-> to the SSRC to check
 *
 * Return:      0: SSRC already exist
 *				1: SSRC is unique
 ---------------------------------------------------------------------------*/
 DWORD isSSRCunique (PSSRC_ENTRY pSSRC, 
					 DWORD *SSRCnum)
	{
	IN_OUT_STR ("RTCP: Enter isSSRCunique()\n");

	// make sure SSRC is unique for this session 
	while (pSSRC)
		{
		if (pSSRC->SSRC == *SSRCnum)
			{
			// SSRC already in use, get a new one 
			*SSRCnum = 0;
			return FALSE;
			}
					         
		// get next RTCP session 
		pSSRC = (PSSRC_ENTRY)pSSRC->SSRCList.next;
		}

	IN_OUT_STR ("RTCP: Exit isSSRCunique()\n");

	return TRUE;
	}                                                                                     
                                                                              
                                                                              
/*----------------------------------------------------------------------------
 * Function   : createSSRCEntry
 * Description: Create an SSRC entry, for a particular RTP/RTCP session
 * 
 * Input :      SSRCnum		: SSRC number
 *				pRTCP		: -> to the RTCP session
 *				fromAddr	: From address
 *				fromLen		: From length
 *				headOfList	: Put the new entry at the head of the list
 *
 * Return:      Address of the SSRC entry data structure.
 ---------------------------------------------------------------------------*/
 PSSRC_ENTRY createSSRCEntry (DWORD SSRCnum, 
							  PRTCP_SESSION pRTCP,
 							  PSOCKADDR fromAddr, 
							  DWORD fromLen, 
							  DWORD headOfList)
	{               
	PSSRC_ENTRY		pSSRCentry;
	PSSRC_ENTRY		pSSRCtmp;
	PLINK_LIST		pTmp;
	BOOL			entryAdded = FALSE;

	IN_OUT_STR ("RTCP: Enter createSSRCEntry()\n");

	// get an SSRC cell from the free list 
	pSSRCentry = getOneSSRCentry (&pRTCPContext->RRCMFreeStat, 
								  pRTCPContext->hHeapRRCMStat,
								  &pRTCPContext->dwInitNumFreeRRCMStat,
								  &pRTCPContext->critSect);
	if (pSSRCentry == NULL)
		return NULL;

	// save the remote source address
	if (saveNetworkAddress(pSSRCentry,
						   fromAddr,
						   fromLen) != RRCM_NoError)
		{
		addToHeadOfList (&pRTCPContext->RRCMFreeStat, 
						 (PLINK_LIST)pSSRCentry,
						 &pRTCPContext->critSect);
		return (NULL);
		}

	pSSRCentry->SSRC = SSRCnum;
	pSSRCentry->rcvInfo.dwProbation = MIN_SEQUENTIAL;

	// set this SSRC entry's RTCP session
	pSSRCentry->pRTCPses  = pRTCP;

	// initialize the socket descriptor
	pSSRCentry->RTPsd  = ((PSSRC_ENTRY)pRTCP->XmtSSRCList.prev)->RTPsd;
	pSSRCentry->RTCPsd = ((PSSRC_ENTRY)pRTCP->XmtSSRCList.prev)->RTCPsd;

	// initialize 'dwLastReportRcvdTime' to now
	pSSRCentry->dwLastReportRcvdTime = timeGetTime();

#ifdef _DEBUG
	wsprintf (debug_string, 
	  "RTCP: Create SSRC entry (Addr:x%lX, SSRC=x%lX) for session: (Addr:x%lX)",
	  pSSRCentry, pSSRCentry->SSRC, pRTCP);
	RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif

#ifdef ENABLE_ISDM2
	// register to ISDM
	if (Isdm2.hISDMdll && (pRTCP->dwSessionStatus & RTCP_DEST_LEARNED))
		registerSessionToISDM (pSSRCentry, pRTCP, &Isdm2);
#endif

	// check to see if it's our entry that needs to be put at the head of 
	//	the SSRC list. If it's not our entry, will find a place for it in the
	//	ordered list
	if (headOfList)
		{
		// Attach the SSRC to the RTCP session list entry head 
		addToHeadOfList (&pRTCP->XmtSSRCList, 
						 (PLINK_LIST)pSSRCentry,
						 &pRTCP->critSect);

		// number of SSRC entry for the RTCP session 
		InterlockedIncrement ((long *)&pRTCP->dwCurNumSSRCperSes);

#ifdef MONITOR_STATS
		// high number of SSRC entry for the RTCP session
		InterlockedIncrement ((long *)&pRTCP->dwHiNumSSRCperSes)
#endif

		return (pSSRCentry);
		}

	// put it on the receive list of SSRCs
	pTmp = (PLINK_LIST)pRTCP->RcvSSRCList.prev;

	// check if it's the first one
	if (pTmp == NULL)
		{
		// Attach the SSRC to the RTCP session list entry head 
		addToHeadOfList (&pRTCP->RcvSSRCList, 
						 (PLINK_LIST)pSSRCentry,
						 &pRTCP->critSect);

		// number of SSRC entry for the RTCP session 
		InterlockedIncrement ((long *)&pRTCP->dwCurNumSSRCperSes);

#ifdef MONITOR_STATS
		// high number of SSRC entry for the RTCP session 
		InterlockedIncrement ((long *)&pRTCP->dwHiNumSSRCperSes)
#endif

		return (pSSRCentry);
		}

	while (!entryAdded)
		{
		if (pTmp != NULL)
			{
			pSSRCtmp = (PSSRC_ENTRY)pTmp;
			if (pSSRCtmp->SSRC < SSRCnum)
				pTmp = pTmp->next;
			else
				{
				// lock at the RTCP session level, for head/tail ptrs access
				EnterCriticalSection (&pRTCP->critSect);

				if ((pTmp->next == NULL) && (pSSRCtmp->SSRC < SSRCnum))
					{
					// attach the SSRC to the RTCP session list entry head 
					// This SSRC is bigger than all other ones
					addToHeadOfList (&pRTCP->RcvSSRCList, 
									 (PLINK_LIST)pSSRCentry,
									 &pRTCP->critSect);
					}
				else if (pTmp->prev == NULL)
					{
					// attach the SSRC to the RTCP session list entry tail 
					// This SSRC is smaller than all other ones
					addToTailOfList (&pRTCP->RcvSSRCList, 
									 (PLINK_LIST)pSSRCentry,
									 &pRTCP->critSect);
					}
				else
					{				
					// this SSRC is in between other SSRCs
					EnterCriticalSection (&((PSSRC_ENTRY)pTmp->prev)->critSect);
					(pTmp->prev)->next = (PLINK_LIST)pSSRCentry;
					LeaveCriticalSection (&((PSSRC_ENTRY)pTmp->prev)->critSect);

					// don't need to lock out pSSRCentry pointers
					pSSRCentry->SSRCList.next = pTmp;
					pSSRCentry->SSRCList.prev = pTmp->prev;

					pTmp->prev = (PLINK_LIST)pSSRCentry;
					}

				// unlock RTCP session access 
				LeaveCriticalSection (&pRTCP->critSect);

				// set loop flag 
				entryAdded = TRUE;
				}
			}
		else
			{
			// attach the SSRC to the RTCP session list entry head 
			addToHeadOfList (&pRTCP->RcvSSRCList, 
							 (PLINK_LIST)pSSRCentry,
							 &pRTCP->critSect);

			// set loop flag 
			entryAdded = TRUE;
			}
		}

	// number of SSRC entry for the RTCP session 
	InterlockedIncrement ((long *)&pRTCP->dwCurNumSSRCperSes);

#ifdef MONITOR_STATS
	// high number of SSRC entry for the RTCP session 
	InterlockedIncrement ((long *)&pRTCP->dwHiNumSSRCperSes)
#endif

	IN_OUT_STR ("RTCP: Exit createSSRCEntry()\n");

	return (pSSRCentry);
	}
                                                                                                                                                            
                                                                              
                                                                              
/*----------------------------------------------------------------------------
 * Function   : deleteSSRCEntry
 * Description: Delete an SSRC entry (for a particular RTP/RTCP session).
 * 
 * Input :      SSRCnum		: SSRC number to delete from the list
 *				pRTCP		: -> to the RTCP session
 *
 * Return:      TRUE:	Deleted
 *				FALSE:	Entry not found
 ---------------------------------------------------------------------------*/
 DWORD deleteSSRCEntry (DWORD SSRCnum, 
						PRTCP_SESSION pRTCP)
	{               
	PSSRC_ENTRY	pSSRCtmp = NULL;
	PLINK_LIST	pTmp;
	DWORD		dwStatus = FALSE;

	IN_OUT_STR ("RTCP: Enter deleteSSRCEntry()\n");

	// walk through the list from the tail 
	pTmp = (PLINK_LIST)pRTCP->RcvSSRCList.prev;

	while (pTmp)
		{
		// lock access to this entry 
		EnterCriticalSection (&((PSSRC_ENTRY)pTmp)->critSect);

		if (((PSSRC_ENTRY)pTmp)->SSRC == SSRCnum)
			{
#ifdef _DEBUG
			wsprintf (debug_string, 
					  "RTCP: Delete SSRC=x%lX from session: (Addr:x%lX)",
					  SSRCnum, pRTCP);
			RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif

#ifdef ENABLE_ISDM2
			// unregister ISDM session
			if (Isdm2.hISDMdll && ((PSSRC_ENTRY)pTmp)->hISDM)
				Isdm2.ISDMEntry.ISD_DeleteValue(hRRCMRootKey, 
												((PSSRC_ENTRY)pTmp)->hISDM, NULL);
#endif

			// remove the entry from the list 
			if (pTmp->next == NULL)
				{
				removePcktFromHead (&pRTCP->RcvSSRCList,
									&pRTCP->critSect);
				}
			else if (pTmp->prev == NULL)
				{
				removePcktFromTail (&pRTCP->RcvSSRCList,
									&pRTCP->critSect);
				}
			else
				{
				// in between, relink around 
				EnterCriticalSection (&((PSSRC_ENTRY)pTmp->prev)->critSect);
				(pTmp->prev)->next = pTmp->next;
				LeaveCriticalSection (&((PSSRC_ENTRY)pTmp->prev)->critSect);

				EnterCriticalSection (&((PSSRC_ENTRY)pTmp->next)->critSect);
				(pTmp->next)->prev = pTmp->prev;
				LeaveCriticalSection (&((PSSRC_ENTRY)pTmp->next)->critSect);
				}

			// number of SSRC entry for the RTCP session 
			InterlockedDecrement ((long *)&pRTCP->dwCurNumSSRCperSes);

			// return entry to the free list 
			addToHeadOfList (&pRTCPContext->RRCMFreeStat, 
							 pTmp,
							 &pRTCPContext->critSect);

			// unlock access to this entry 
			LeaveCriticalSection (&((PSSRC_ENTRY)pTmp)->critSect);

			dwStatus = TRUE;
			break;
			}

		// unlock access to this entry 
		LeaveCriticalSection (&((PSSRC_ENTRY)pTmp)->critSect);

		pTmp = pTmp->next;
		}	

	IN_OUT_STR ("RTCP: Exit deleteSSRCEntry()\n");	

	return (dwStatus);
	}

 
/*----------------------------------------------------------------------------
 * Function   : deleteSSRClist
 * Description: Delete the SSRC list of an RTP/RTCP session.
 * 
 * Input :      pRTCP	  : -> to RTCP session
 *				pFreeList : -> to the free list of SSRCs
 *				pOwner	  : -> to the free list owner
 *
 * Return:      None
 ---------------------------------------------------------------------------*/
 void deleteSSRClist (PRTCP_SESSION pRTCP, 
					  PLINK_LIST pFreeList, 
					  PRTCP_CONTEXT pOwner)
	{               
	PLINK_LIST	pSSRC;

	IN_OUT_STR ("RTCP: Enter deleteSSRClist()\n");

	ASSERT (pFreeList);
	ASSERT (pRTCP);

	// lock access to the full RTCP session 
	EnterCriticalSection (&pRTCP->critSect);

	// go through the list of transmit SSRCs for this RTCP session 
	while (pRTCP->XmtSSRCList.next != NULL)
		{
		// get packet from the list tail 
		pSSRC = removePcktFromTail ((PLINK_LIST)&pRTCP->XmtSSRCList,
									&pRTCP->critSect);
		if (pSSRC != NULL)
			{
#ifdef _DEBUG
			wsprintf(debug_string, 
					 "RTCP: Delete SSRC entry (x%lX) from session (x%lX)",
					 ((PSSRC_ENTRY)pSSRC)->SSRC, pRTCP);
			RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif					

#ifdef ENABLE_ISDM2
			// unregister ISDM session
			if (Isdm2.hISDMdll && ((PSSRC_ENTRY)pSSRC)->hISDM)
				Isdm2.ISDMEntry.ISD_DeleteValue (hRRCMRootKey, 
										((PSSRC_ENTRY)pSSRC)->hISDM, NULL);
#endif

			// put it back to the free list 
			addToHeadOfList (pFreeList, pSSRC, &pOwner->critSect);

			// release the critical section
			DeleteCriticalSection (&((PSSRC_ENTRY)pSSRC)->critSect);
			}
		}

	// go through the list of SSRCs for this RTP/RTCP session 
	while (pRTCP->RcvSSRCList.next != NULL)
		{
		// get packet from the list tail 
		pSSRC = removePcktFromTail ((PLINK_LIST)&pRTCP->RcvSSRCList,
									&pRTCP->critSect);
		if (pSSRC != NULL)
			{
#ifdef _DEBUG
			wsprintf(debug_string, 
					 "RTCP: Delete SSRC entry (x%lX) from session (x%lX)",
					 ((PSSRC_ENTRY)pSSRC)->SSRC, pRTCP);
			RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif					

#ifdef ENABLE_ISDM2
			// unregister ISDM session
			if (Isdm2.hISDMdll && ((PSSRC_ENTRY)pSSRC)->hISDM)
				Isdm2.ISDMEntry.ISD_DeleteValue (hRRCMRootKey, 
								((PSSRC_ENTRY)pSSRC)->hISDM, NULL);
#endif

			// put it back to the free list 
			addToHeadOfList (pFreeList, pSSRC, &pOwner->critSect);

			// release the critical section
			DeleteCriticalSection (&((PSSRC_ENTRY)pSSRC)->critSect);
			}
		}

	// unlock access to the full RTCP session 
	LeaveCriticalSection (&pRTCP->critSect);

	IN_OUT_STR ("RTCP: Exit deleteSSRClist()\n");				
	}


/*----------------------------------------------------------------------------
 * Function   : SSRCTimeoutCheck
 * Description: Check if an rcv SSRC needs to be timed out
 *				Since there may be multiple RCV SSRCs, repeat calling
 *				this function until it returns NULL
 * 
 * Input :      pRTCC	: -> to the RTCP session
 *				curTime	: Current time
 *
 * Return: 		NULL  : No action needed
 *				PSSRC : -> to the SSRC entry that should be deleted
 ---------------------------------------------------------------------------*/
PSSRC_ENTRY SSRCTimeoutCheck (PRTCP_SESSION pRTCP, DWORD curTime) 
	{
	PSSRC_ENTRY pSSRC;
	DWORD		tmpTime;

	// check the colliding entries table and clear it if needed
	RRCMTimeOutCollisionTable (pRTCP);

	// get the right session to close 
	pSSRC = (PSSRC_ENTRY)pRTCP->RcvSSRCList.prev;
	while (pSSRC)
		{
		// check if this SSRC timed-out
		tmpTime = curTime - pSSRC->dwLastReportRcvdTime;

		// get the time in minutes
		tmpTime /= (1000*60);
		if (tmpTime > RTCP_TIME_OUT_MINUTES)
			{
				break;
			}

		pSSRC = (PSSRC_ENTRY)pSSRC->SSRCList.next;
		}
	return pSSRC;
	}


/*---------------------------------------------------------------------------
 * Function   : RRCMChkCollisionTable
 * Description: Check the collision table to try to find a match 
 * 
 * Input :	pFrom		:	-> recv from address
 *			fromlen		:	-> recv from address length
 *			pSSRC		:	-> to the SSRC entry
 *
 * Return:	TRUE:	Match found
 *			FALSE:	No match found
 --------------------------------------------------------------------------*/
 DWORD RRCMChkCollisionTable (PSOCKADDR pFrom,
 							  UINT fromlen,
							  PSSRC_ENTRY pSSRC)
	{
	DWORD			idx;
	DWORD			dwStatus = FALSE;
	PRTCP_SESSION	pRTCP = pSSRC->pRTCPses;

	IN_OUT_STR ("RRCM: Enter RRCMChkCollisionTable()\n");		 	

	// entry w/ time == 0 are empty
	for (idx = 0; idx < NUM_COLLISION_ENTRIES; idx++)
		{
		if (pRTCP->collInfo[idx].dwCollideTime != 0)
			{
			if (memcmp (&pRTCP->collInfo[idx].collideAddr,
						pFrom, 
						fromlen) == 0)
				{
				// update the time of last collision received
				pRTCP->collInfo[idx].dwCollideTime = timeGetTime();

				dwStatus = TRUE;
				break;
				}
			}
		}

	IN_OUT_STR ("RRCM: Exit RRCMChkCollisionTable()\n");		 	

	return dwStatus;
	}


/*---------------------------------------------------------------------------
 * Function   : RRCMAddEntryToCollisionTable
 * Description: Add an entry into the collision table.
 * 
 * Input :	pFrom		:	-> recv from address
 *			fromlen		:	-> recv from address length
 *			pSSRC		:	-> to the SSRC entry
 *
 * Return:	TRUE:	Entry added
 *			FALSE:	Table full
 --------------------------------------------------------------------------*/
 DWORD RRCMAddEntryToCollisionTable (PSOCKADDR pFrom,
 									 UINT fromlen,
									 PSSRC_ENTRY pSSRC)
	{
	DWORD			idx;
	DWORD			dwStatus = FALSE;
	PRTCP_SESSION	pRTCP = pSSRC->pRTCPses;

	IN_OUT_STR ("RRCM: Enter RRCMAddEntryToCollisionTable()\n");
	
	// entry w/ time == 0 are empty
	for (idx = 0; idx < NUM_COLLISION_ENTRIES; idx++)
		{
		if (pRTCP->collInfo[idx].dwCollideTime == 0)
			{
			memcpy (&pRTCP->collInfo[idx].collideAddr,
					pFrom, 
					fromlen);

			pRTCP->collInfo[idx].addrLen = fromlen;
			pRTCP->collInfo[idx].dwCollideTime = timeGetTime();
			pRTCP->collInfo[idx].dwCurRecvRTCPrptNumber = pSSRC->dwNumRptRcvd;

			pRTCP->collInfo[idx].SSRC = pSSRC->SSRC;

			dwStatus = TRUE;
			break;
			}
		}

	
	IN_OUT_STR ("RRCM: Exit RRCMAddEntryToCollisionTable()\n");		 

	return dwStatus;
	}



/*---------------------------------------------------------------------------
 * Function   : RRCMTimeOutInCollisionTable
 * Description: Check if an entry in the collision table must be timed-out
 * 
 * Input :	pRTCP	:	-> to the RTCP session
 *
 * Return:	None
 --------------------------------------------------------------------------*/
 void RRCMTimeOutCollisionTable (PRTCP_SESSION pRTCP)
	{
	DWORD	idx;
	DWORD	currTime = timeGetTime();
	DWORD	diffTime;

	IN_OUT_STR ("RTCP: Enter RRCMTimeOutCollisionTable()\n");
	
	// entry w/ time == 0 are empty
	for (idx = 0; idx < NUM_COLLISION_ENTRIES; idx++)
		{
		// valid entries have the time set
		if (pRTCP->collInfo[idx].dwCollideTime)
			{
			// remove the entry from this table if 10 RTCP report intervals
			// have occured without a collision

			// clear the entry if over 5'
// !!! TODO !!!
// !!! using the right interval !!!
			diffTime = currTime - pRTCP->collInfo[idx].dwCollideTime;
			diffTime /= 1000;
			if (diffTime > 300)
				{
				pRTCP->collInfo[idx].dwCollideTime = 0;

				// the SSRC entry in the receive list will be deleted by
				// the timeout thread
				}
			}
		}

	IN_OUT_STR ("RTCP: Exit RRCMTimeOutCollisionTable()\n");		 
	}


/*----------------------------------------------------------------------------
 * Function   : clearSSRCEntry
 * Description: Clears what needs to be cleared in an SSRC entry
 * 
 * Input :      pSSRC		: -> to the SSRC entry
 *
 * Return:      None
 ---------------------------------------------------------------------------*/
 void clearSSRCEntry (PSSRC_ENTRY pSSRC)
	{
	IN_OUT_STR ("RTCP: Enter clearSSRCEntry()\n");				

	memset (&pSSRC->xmtInfo, 0x00, sizeof(XMIT_INFO));
	memset (&pSSRC->rcvInfo, 0x00, sizeof(RECV_INFO));
	memset (&pSSRC->rrFeedback, 0x00, sizeof (RTCP_FEEDBACK));
	memset (&pSSRC->cnameInfo, 0x00, sizeof(SDES_DATA));
	memset (&pSSRC->nameInfo, 0x00, sizeof(SDES_DATA));
	memset (&pSSRC->from, 0x00, sizeof(SOCKADDR));

	pSSRC->SSRC					= 0;			
	pSSRC->dwSSRCStatus			= 0;
	pSSRC->dwStreamClock		= 0;
	pSSRC->fromLen				= 0;
	pSSRC->dwLastReportRcvdTime	= 0;
	pSSRC->dwUserXmtTimeoutCtrl = 0;
	pSSRC->RTPsd				= 0;
	pSSRC->RTCPsd				= 0;
	pSSRC->pRTCPses				= NULL;
	pSSRC->dwNumRptSent			= 0;
	pSSRC->dwNumRptRcvd			= 0;

#ifdef ENABLE_ISDM2
	pSSRC->hISDM				= 0;
#endif

#ifdef _DEBUG
	pSSRC->dwPrvTime			= 0;	
#endif

	IN_OUT_STR ("RTCP: Exit clearSSRCEntry()\n");				
	}

                                                                              
// [EOF]