/*
	RVSTREAM.C
*/

#include "precomp.h"

#define PLAYOUT_DELAY_FACTOR	2
#ifndef MAX_MISORDER
#define MAX_MISORDER 30
#endif

void FreeNetBufList(NETBUF *pNB, IRTPRecv *pRTP)
{
	NETBUF *pNBTemp;
	while (pNB) {
		pNBTemp = pNB;
		pNB = pNB->next;
		if (pRTP) pRTP->FreePacket(*(WSABUF **)(pNBTemp + 1));
		pNBTemp->pool->ReturnBuffer(pNBTemp);
	}	
}

void AppendNetBufList(NETBUF *pFirstNB, NETBUF *pNB)
{
	NETBUF *pNB1 = pFirstNB;
	while (pNB1->next) {
		ASSERT(pNB != pNB1);
		pNB1 = pNB1->next;
	}
	ASSERT(pNB != pNB1);
	pNB1->next = pNB;
}



int RVStream::Initialize(UINT flags, UINT size, IRTPRecv *pRTP, MEDIAPACKETINIT *papi, ULONG ulSamplesPerPacket, ULONG ulSamplesPerSec, VcmFilter *pVideoFilter)
{
	m_pVideoFilter = pVideoFilter;
	return ((RxStream*)this)->Initialize(flags, size, pRTP, papi, ulSamplesPerPacket, ulSamplesPerSec);
}




/*
	Queues a received RTP packet.
	The packet is described by pNetBuf.
	This routine will take care of freeing pNetBuf (even in error cases)
*/
HRESULT
RVStream::PutNextNetIn(WSABUF *pWsaBuf, DWORD timestamp, UINT seq, UINT fMark, BOOL *pfSkippedData, BOOL *pfSyncPoint)
{
	FX_ENTRY("RVStream::PutNextNetIn");

	UINT pos;
	MediaPacket *pAP;
	NETBUF *pNB_Packet;
	HRESULT hr;
	NETBUF *pNetBuf = (NETBUF *)m_NetBufPool.GetBuffer();
	ASSERT(pNetBuf);

	EnterCriticalSection(&m_CritSect);

	*pfSkippedData = FALSE;
	*pfSyncPoint = FALSE;

	if (pNetBuf == NULL)
	{
		hr = E_OUTOFMEMORY;
		WARNING_OUT(("RVStream::PutNextNetIn - Out of memory in buffer pool"));
		m_pRTP->FreePacket(pWsaBuf);
		goto ErrorExit;
	}

	*(WSABUF **)(pNetBuf+1) = pWsaBuf;	// cache the WSABUF pointer so it can be returned later
	pNetBuf->data = (PBYTE) pWsaBuf->buf + sizeof(RTP_HDR);
	pNetBuf->length = pWsaBuf->len - sizeof(RTP_HDR);
	pNetBuf->next = NULL;
	pNetBuf->pool = &m_NetBufPool;

	hr = ReassembleFrame(pNetBuf, seq, fMark);

	if (hr != DPR_SUCCESS)
	{
		// free pNetBuf since its not yet on m_NetBufList.
		// m_NetBufList will be freed at ErrorExit
		::FreeNetBufList(pNetBuf,m_pRTP);
		goto ErrorExit;
	}

	// not the end of the frame
	if (!fMark)
	{
		LeaveCriticalSection(&m_CritSect);
		return S_FALSE;  // success, but not a new frame yet
	}

	// If we get here we think we have a complete encoded video frame (fMark was
	// set on the last packet)
	
	// if the ring is full or the timestamp is earlier, dump everything.This may be too drastic
	// and the reset action could be refined to dump only the older
	// packets. However, need to make sure the ring doesnt get "stuck"
	pos = ModRing(m_MaxPos+1);
	if (pos == m_FreePos || TS_EARLIER(timestamp, m_MaxT)) {
		Reset(seq,timestamp);
		*pfSkippedData = TRUE;
		pos = ModRing(m_MaxPos + 1); // check again
		if (pos == m_FreePos) {
			hr = DPR_OUT_OF_MEMORY;
			m_LastGoodSeq -= MAX_MISORDER; //make sure we dont accidentally synchronize
			goto ErrorExit;
		}
	}

	// insert frame into ring

	pAP = m_Ring[pos];
	if (pAP->Busy() || pAP->GetState() != MP_STATE_RESET) {
		hr = DPR_DUPLICATE_PACKET;
		goto ErrorExit;
	}

	// new stuff
	hr = RestorePacket(m_NetBufList, pAP, timestamp, seq, fMark, pfSyncPoint);
	if (FAILED(hr))
	{
		goto ErrorExit;
	}

	if (*pfSyncPoint)
	{
		DEBUGMSG (ZONE_IFRAME, ("%s: Received a keyframe\r\n", _fx_));
	}

	::FreeNetBufList(m_NetBufList,m_pRTP);
	m_NetBufList = NULL;
#ifdef DEBUG
	if (!TS_LATER(timestamp, m_MaxT))
	{
			DEBUGMSG (ZONE_DP, ("PutNextNetIn(): Reconstructed frame's timestamp <= to previous frame's!\r\n"));
	}
#endif
	m_MaxT = timestamp;
	m_MaxPos = pos;		// advance m_MaxPos
// end new stuff

		
	LeaveCriticalSection(&m_CritSect);
	StartDecode();
	return hr;
ErrorExit:
	// if we're in the middle of assembling a frame, free buffers
	if (m_NetBufList){
		::FreeNetBufList(m_NetBufList,m_pRTP);
		m_NetBufList = NULL;
	}
	LeaveCriticalSection(&m_CritSect);
	return hr;

}

// Called to force the release of any accumulated NETBUFs back to the owner (RTP).
// This can be called at shutdown or to escape from a out-of-buffer situation
BOOL RVStream::ReleaseNetBuffers()
{
	::FreeNetBufList(m_NetBufList, m_pRTP);
	m_NetBufList = NULL;
	return TRUE;
}

// Take a packet and reassemble it into a frame.
// Doesnt currently process out-of-order packets (ie) the entire frame is
// discarded
// The NETBUF is held onto, unless an error is returned
HRESULT
RVStream::ReassembleFrame(NETBUF *pNetBuf, UINT seq, UINT fMark)
{

	++m_LastGoodSeq;
	if (seq != m_LastGoodSeq) {
		// dont handle out of sequence packets
		if (fMark)
			m_LastGoodSeq = (WORD)seq;
		else
			--m_LastGoodSeq;	// LastGoodSeq left unchanged

		return DPR_OUT_OF_SEQUENCE;
	}

	
	if (m_NetBufList ) {
		// append to list of fragments
		::AppendNetBufList(m_NetBufList,pNetBuf);
	} else {
		// start of frame
		m_NetBufList = pNetBuf;
	}

	return DPR_SUCCESS;	
}

HRESULT
RVStream::SetLastGoodSeq(UINT seq)
{
	m_LastGoodSeq = seq ? (WORD)(seq-1) : (WORD)0xFFFF;
	return DPR_SUCCESS;
}

// called when restarting after a pause (fSilenceOnly == FALSE) or
// to catch up when latency is getting too much (fSilenceOnly == TRUE)
// determine new play position by skipping any
// stale packets

HRESULT RVStream::FastForward( BOOL fSilenceOnly)
{
	UINT pos;
	DWORD timestamp = 0;
	// restart the receive stream
	EnterCriticalSection(&m_CritSect);
	if (!TS_EARLIER(m_MaxT , m_PlayT)) {
		// there are buffers waiting to be played
		// dump them!
		if (ModRing(m_MaxPos - m_PlayPos) <= m_DelayPos)
			goto Exit;	// not too many stale packets;

		for (pos=m_PlayPos;pos != ModRing(m_MaxPos -m_DelayPos);pos = ModRing(pos+1),m_PlaySeq++) {
			if (m_Ring[pos]->Busy()
				|| (m_Ring[pos]->GetState() != MP_STATE_RESET
					&& (fSilenceOnly ||ModRing(m_MaxPos-pos) <= m_MaxDelayPos)))
			{	// non-empty packet
				if (m_Ring[pos]->Busy())	// uncommon case
					goto Exit;	// bailing out
				timestamp = m_Ring[pos]->GetTimestamp();
				break;
			}
			m_Ring[pos]->Recycle();	// free NETBUF and Reset state
			LOG((LOGMSG_RX_SKIP,pos));
		}
		if (timestamp)	{// starting from non-empty packet
			m_PlayT = timestamp;
			//m_Ring[pos]->GetProp(MP_PROP_SEQNUM, &m_PlaySeq);
		} else {		// starting from (possibly) empty packet
			m_PlayT++;
		}

		// probably also need to update FreePos
		if (m_FreePos == ModRing(m_PlayPos-1))
			m_FreePos = ModRing(pos-1);
		m_PlayPos = pos;
		/*
		if (pos == ModRing(m_MaxPos+1)) {
			DEBUGMSG(1,("Reset:: m_MaxT inconsisten!\n"));
		}
		*/
		LOG((LOGMSG_RX_RESET2,m_MaxT,m_PlayT,m_PlayPos));
	}
Exit:
	LeaveCriticalSection(&m_CritSect);
	return DPR_SUCCESS;
}


HRESULT
RVStream::Reset(UINT seq,DWORD timestamp)
{
	UINT pos;
	HRESULT hr;
	// restart the receive stream
	EnterCriticalSection(&m_CritSect);
	LOG((LOGMSG_RX_RESET,m_MaxPos,m_PlayT,m_PlayPos));
	/*if (!TS_EARLIER(m_MaxT , m_PlayT)) */
	{
		// there are buffers waiting to be played
		// dump them!
		// Empty the RVStream and set PlayT appropriately
		for (pos = m_PlayPos;
			pos != m_FreePos;
			pos = ModRing(pos+1))
		{
			if (m_Ring[pos]->Busy ())
			{
				DEBUGMSG (1, ("RVStream::Reset: packet is busy, pos=%d\r\n", pos));
				ASSERT(1);
				hr = DPR_INVALID_PARAMETER;
				goto Failed;
			}
			m_Ring[pos]->Recycle();	// free NETBUF and Reset state
		}
	}
	m_MaxPos = ModRing(m_PlayPos-1);
	m_PlayT = timestamp;
	m_MaxT = m_PlayT -1;	// m_MaxT must be less than m_PlayT
	m_PlaySeq = seq;
	
	LOG((LOGMSG_RX_RESET2,m_MaxPos,m_PlayT,m_PlayPos));
	hr = DPR_SUCCESS;
Failed:
	LeaveCriticalSection(&m_CritSect);
	return hr;		
}

MediaPacket *RVStream::GetNextPlay(void)
{
	MediaPacket *pAP = NULL;
	UINT pos,seq;
	DWORD timestamp = 0, dwVal;
	EnterCriticalSection(&m_CritSect);


	pAP = m_Ring[m_PlayPos];
	if (pAP->Busy() ||
	(pAP->GetState() != MP_STATE_RESET && pAP->GetState() != MP_STATE_DECODED)
	 || ModRing(m_PlayPos+1) == m_FreePos) {
		LeaveCriticalSection(&m_CritSect);
		return NULL;
	} else {
	// If there are empty buffer(s) at the head of the q followed
	// by  a talkspurt (non-empty buffers) and if the talkspurt is excessively
	// delayed then squeeze out the silence.
	//
		if (pAP->GetState() == MP_STATE_RESET)
			FastForward(TRUE);	// skip silence packets if necessary
		pAP = m_Ring[m_PlayPos];	// in case the play position changed
		if (pAP->GetState() == MP_STATE_DECODED) {
			timestamp = pAP->GetTimestamp();
			seq = pAP->GetSeqNum();
		}
			
	}

	pAP->Busy(TRUE);
	m_PlayPos = ModRing(m_PlayPos+1);
	if (timestamp) {
		m_PlayT = timestamp+1;
		m_PlaySeq = seq+1;
	} else {
		m_PlaySeq++;
		// we dont really know the timestamp of the next frame to play
		// without looking at it, and it may not have arrived
		// so m_PlayT is just a lower bound
		m_PlayT++;	
	}
	LeaveCriticalSection(&m_CritSect);
	return pAP;
}

RVStream::Destroy()
{
	ASSERT (!m_NetBufList);
	//::FreeNetBufList(m_NetBufList,m_pRTP);
	m_NetBufList = NULL;
	RxStream::Destroy();
	return DPR_SUCCESS;
}


void RVStream::StartDecode()
{
	MediaPacket *pVP;
	MMRESULT mmr;

	// if we have a separate decode thread this will signal it.
	// for now we insert the decode loop here
	while (pVP = GetNextDecode())
	{
		mmr = m_pVideoFilter->Convert((VideoPacket*)pVP, VP_DECODE);
		if (mmr != MMSYSERR_NOERROR)
			pVP->Recycle();
		else
			pVP->SetState(MP_STATE_DECODED);

		Release(pVP);
	}
}


HRESULT RVStream::RestorePacket(NETBUF *pNetBuf, MediaPacket *pVP, DWORD timestamp, UINT seq, UINT fMark, BOOL *pfReceivedKeyframe)
{
	VOID *pNet;
	UINT uSizeNet;
	WSABUF bufDesc[MAX_VIDEO_FRAGMENTS];		// limit to at most 32 fragments
	UINT i;
	DWORD dwReceivedBytes=0;
	NETBUF *pnb;
	DWORD dwLength;
    DWORD_PTR dwPropVal;
	MMRESULT mmr;

	i = 0;
	pnb = pNetBuf;
	while (pnb && i < MAX_VIDEO_FRAGMENTS) {
		bufDesc[i].buf = (char *)pnb->data;
		bufDesc[i].len = pnb->length;
		dwReceivedBytes += pnb->length + sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE;
		pnb = pnb->next;
		i++;
	}
	ASSERT(!pnb); // fail if we get a frame with more than MAX_VIDEO_FRAGMENTS

    // Write the bits per second counter
    UPDATE_COUNTER(g_pctrVideoReceiveBytes, dwReceivedBytes * 8);


	pVP->GetNetData(&pNet, &uSizeNet);

	// Initialize length to maximum reconstructed frame size
	pVP->GetProp(MP_PROP_MAX_NET_LENGTH, &dwPropVal);
    dwLength = (DWORD)dwPropVal;

	if (pnb==NULL)
	{
		mmr = m_pVideoFilter->RestorePayload(bufDesc, i, (BYTE*)pNet, &dwLength, pfReceivedKeyframe);
		if (mmr == MMSYSERR_NOERROR)
		{
			pVP->SetNetLength(dwLength);
			pVP->Receive(NULL, timestamp, seq, fMark);
			return S_OK;
		}
	}

	return E_FAIL;
}