Windows-Server-2003/enduser/netmeeting/av/nac/rvstream.cpp

425 lines
10 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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;
}