425 lines
10 KiB
C++
425 lines
10 KiB
C++
|
/*
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
|