|
|
/*
RXSTREAM.C */
#include "precomp.h"
extern UINT g_MinWaveAudioDelayMs; // minimum millisecs of introduced playback delay
extern UINT g_MaxAudioDelayMs; // maximum milliesecs of introduced playback delay
RxStream::RxStream(UINT size) { UINT i; for (i =0; i < size; i++) { m_Ring[i] = NULL; } // initialize object critical section
InitializeCriticalSection(&m_CritSect); }
RxStream::~RxStream() { DeleteCriticalSection(&m_CritSect); }
RxStream::Initialize( UINT flags, UINT size, // MB power of 2
IRTPRecv *pRTP, MEDIAPACKETINIT *papi, ULONG ulSamplesPerPacket, ULONG ulSamplesPerSec, AcmFilter *pAcmFilter) // this param may be NULL for video
{ UINT i; MediaPacket *pAP;
m_fPreamblePacket = TRUE; m_pDecodeBufferPool = NULL;
m_RingSize = size; m_dwFlags = flags; if (flags & DP_FLAG_MMSYSTEM) { if (m_RingSize > MAX_RXRING_SIZE) return FALSE; } else if (flags & DP_FLAG_VIDEO) { if (m_RingSize > MAX_RXVRING_SIZE) return FALSE; if (!IsSameFormat (papi->pStrmConvSrcFmt, papi->pStrmConvDstFmt)) { // the video decode bufs are not allocated per MediaPacket object.
// instead we use a BufferPool with a few buffers.
papi->fDontAllocRawBufs = TRUE;
DBG_SAVE_FILE_LINE m_pDecodeBufferPool = new BufferPool; // Three seems to be the minimum number of frame bufs
// One is being rendered and at least two are needed
// so the rendering can catch up with the received frames
// (another alternative is to dump frames to catch up)
if (m_pDecodeBufferPool->Initialize(3, sizeof(NETBUF)+papi->cbSizeRawData + papi->cbOffsetRawData) != S_OK) { DEBUGMSG(ZONE_DP,("Couldnt initialize decode bufpool!\n")); delete m_pDecodeBufferPool; m_pDecodeBufferPool = NULL; return FALSE; } } }
m_pRTP = pRTP;
for (i=0; i < m_RingSize; i++) { if (flags & DP_FLAG_MMSYSTEM) { DBG_SAVE_FILE_LINE pAP = new AudioPacket; } else if (flags & DP_FLAG_VIDEO) { DBG_SAVE_FILE_LINE pAP = new VideoPacket; } m_Ring[i] = pAP; papi->index = i; if (!pAP || pAP->Initialize(papi) != DPR_SUCCESS) break; } if (i < m_RingSize) { for (UINT j=0; j<=i; j++) { if (m_Ring[j]) { m_Ring[j]->Release(); delete m_Ring[j]; } } return FALSE; }
m_SamplesPerPkt = ulSamplesPerPacket; m_SamplesPerSec = ulSamplesPerSec; // initialize pointers
m_PlaySeq = 0; m_PlayT = 0; m_MaxT = m_PlayT - 1; // m_MaxT < m_PlayT indicates queue is empty
m_MaxPos = 0; m_PlayPos = 0; m_FreePos = m_RingSize - 1; m_MinDelayPos = m_SamplesPerSec*g_MinWaveAudioDelayMs/1000/m_SamplesPerPkt; // fixed 250 ms delay
if (m_MinDelayPos < 3) m_MinDelayPos = 3; m_MaxDelayPos = m_SamplesPerSec*g_MaxAudioDelayMs/1000/m_SamplesPerPkt; //fixed 750 ms delay
m_DelayPos = m_MinDelayPos; m_ScaledAvgVarDelay = 0; m_SilenceDurationT = 0; //m_DeltaT = MAX_TIMESTAMP;
m_pAudioFilter = pAcmFilter;
// go ahead and cache the WAVEFORMATEX structures
// it's handy to have around
if (m_dwFlags & DP_FLAG_AUDIO) { m_wfxSrc = *(WAVEFORMATEX*)(papi->pStrmConvSrcFmt); m_wfxDst = *(WAVEFORMATEX*)(papi->pStrmConvDstFmt); } m_nBeeps = 0;
return TRUE; }
#define PLAYOUT_DELAY_FACTOR 2
void RxStream::UpdateVariableDelay(DWORD sendT, DWORD arrT) { LONG deltaA, deltaS; DWORD delay,delayPos; // m_ArrivalT0 and m_SendT0 are the arrival and send timestamps of the packet
// with the shortest trip delay. We could have just stored (m_ArrivalT0 - m_SendT0)
// but since the local and remote clocks are completely unsynchronized, there would
// be signed/unsigned complications.
deltaS = sendT - m_SendT0; deltaA = arrT - m_ArrivalT0; if (deltaA < deltaS) { // this packet took less time
delay = deltaS - deltaA; // replace shortest trip delay times
m_SendT0 = sendT; m_ArrivalT0 = arrT; } else { // variable delay is how much longer this packet took
delay = deltaA - deltaS; } // update average variable delay according to
// m_AvgVarDelay = m_AvgVarDelay + (delay - m_AvgVarDelay)*1/16;
// however we are storing the scaled average, with a scaling
// factor of 16. So the calculation becomes
m_ScaledAvgVarDelay = m_ScaledAvgVarDelay + (delay - m_ScaledAvgVarDelay/16); // now calculate delayPos
delayPos = m_MinDelayPos + PLAYOUT_DELAY_FACTOR * m_ScaledAvgVarDelay/16/m_SamplesPerPkt; if (delayPos >= m_MaxDelayPos) delayPos = m_MaxDelayPos;
LOG((LOGMSG_JITTER,delay, m_ScaledAvgVarDelay/16, delayPos)); if (m_DelayPos != delayPos) { DEBUGMSG(ZONE_VERBOSE,("Changing m_DelayPos from %d to %d\n",m_DelayPos, delayPos)); m_DelayPos = delayPos; }
UPDATE_COUNTER(g_pctrAudioJBDelay, m_DelayPos*(m_SamplesPerPkt*1000)/m_SamplesPerSec); }
// This function is only used for audio packets
HRESULT RxStream::PutNextNetIn(WSABUF *pWsaBuf, DWORD timestamp, UINT seq, UINT fMark, BOOL *pfSkippedData, BOOL *pfSyncPoint) { DWORD deltaTicks; MediaPacket *pAP; HRESULT hr; UINT samples; NETBUF netbuf; netbuf.data = (PBYTE) pWsaBuf->buf + sizeof(RTP_HDR); netbuf.length = pWsaBuf->len - sizeof(RTP_HDR); EnterCriticalSection(&m_CritSect);
deltaTicks = (timestamp - m_PlayT)/m_SamplesPerPkt; if (deltaTicks > ModRing(m_FreePos - m_PlayPos)) { // the packet is too late or packet overrun
// if the timestamp is earlier than the max. received so far
// then reject it if there are packets queued up
if (TS_EARLIER(timestamp, m_MaxT) && !IsEmpty()) { hr = DPR_LATE_PACKET; // deltaTicks is -ve
goto ErrorExit; } // restart the receive stream with this packet
Reset(timestamp); m_SendT0 = timestamp; m_ArrivalT0 = MsToTimestamp(timeGetTime()); deltaTicks = (timestamp - m_PlayT)/m_SamplesPerPkt;
}
// insert into ring
pAP = m_Ring[ModRing(m_PlayPos+deltaTicks)]; if (pAP->Busy() || pAP->GetState() != MP_STATE_RESET) { hr = DPR_DUPLICATE_PACKET; goto ErrorExit; } // update number of bits received
UPDATE_COUNTER(g_pctrAudioReceiveBytes,(netbuf.length + sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE)*8);
hr = pAP->Receive(&netbuf, timestamp, seq, fMark); if (hr != DPR_SUCCESS) goto ErrorExit; // m_pRTP->FreePacket(pWsaBuf); // return the buffer to RTP
if (TS_LATER(timestamp, m_MaxT)) { // timestamp > m_MaxT
if (timestamp - m_MaxT > m_SamplesPerPkt * 4) { // probably beginning of talkspurt - reset minimum delay timestamps
// Note: we should use the Mark flag in RTP header to detect this
m_SendT0 = timestamp; m_ArrivalT0 = MsToTimestamp(timeGetTime()); } m_MaxT = timestamp; m_MaxPos = ModRing(m_PlayPos + deltaTicks); } // Calculate variable delay (sort of jitter)
UpdateVariableDelay(timestamp, MsToTimestamp(timeGetTime()));
LeaveCriticalSection(&m_CritSect); StartDecode();
// Some implementations packetize audio in smaller chunks than they negotiated
// We deal with this by checking the length of the decoded packet and change
// the constant m_SamplesPerPkt. Hopefully this will only happen once per session
// (and never for NM-to-NM calls). Randomly varying packet sizes are still going
// to sound lousy, because the recv queue management has the implicit assumption
// that all packets (at least those in the queue) have the same length
if (pAP->GetState() == MP_STATE_DECODED && (samples = pAP->GetDevDataSamples())) { if (samples != m_SamplesPerPkt) { // we're getting different sized (typically smaller) packets than we expected
DEBUGMSG(ZONE_DP,("Changing SamplesPerPkt from %d to %d\n",m_SamplesPerPkt, samples)); m_SamplesPerPkt = samples; m_MinDelayPos = m_SamplesPerSec*g_MinWaveAudioDelayMs/1000/m_SamplesPerPkt; // fixed 250 ms delay
if (m_MinDelayPos < 2) m_MinDelayPos = 2; m_MaxDelayPos = m_SamplesPerSec*g_MaxAudioDelayMs/1000/m_SamplesPerPkt; //fixed 750 ms delay
} } return DPR_SUCCESS; ErrorExit: // m_pRTP->FreePacket(pWsaBuf);
LeaveCriticalSection(&m_CritSect); return hr;
}
// 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 RxStream::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)) { 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; else // starting from (possibly) empty packet
m_PlayT = m_MaxT - m_DelayPos*m_SamplesPerPkt;
// 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 RxStream::Reset(DWORD timestamp) { UINT pos; DWORD T; // restart the receive stream
EnterCriticalSection(&m_CritSect); LOG((LOGMSG_RX_RESET,m_MaxT,m_PlayT,m_PlayPos)); if (!TS_EARLIER(m_MaxT, m_PlayT)) { // there are buffers waiting to be played
// dump them!
// Empty the RxStream and set PlayT appropriately
for (pos = m_PlayPos; pos != ModRing(m_PlayPos-1); pos = ModRing(pos+1)) { if (m_Ring[pos]->Busy ()) { ERRORMESSAGE(("RxStream::Reset: packet is busy, pos=%d\r\n", pos)); ASSERT(1); } T = m_Ring[pos]->GetTimestamp(); m_Ring[pos]->Recycle(); // free NETBUF and Reset state
if (T == m_MaxT) break; } } if (timestamp !=0) m_PlayT = timestamp - m_DelayPos*m_SamplesPerPkt; m_MaxT = m_PlayT - 1; // max must be less than play
LOG((LOGMSG_RX_RESET2,m_MaxT,m_PlayT,m_PlayPos)); LeaveCriticalSection(&m_CritSect); return DPR_SUCCESS; }
BOOL RxStream::IsEmpty() { BOOL fEmpty;
EnterCriticalSection(&m_CritSect); if (TS_EARLIER(m_MaxT, m_PlayT) || m_RingSize == 0) fEmpty = TRUE; else if (m_dwFlags & DP_FLAG_AUTO_SILENCE_DETECT) { UINT pos; // we could have received packets that
// are deemed silent. Walk the packets between
// PlayPos and MaxPos and check if they're all empty
pos = m_PlayPos; fEmpty = TRUE; do { if (m_Ring[pos]->Busy() || (m_Ring[pos]->GetState() != MP_STATE_RESET )) { fEmpty = FALSE; // no point scanning further
break; } pos = ModRing(pos+1); } while (pos != ModRing(m_MaxPos+1)); } else { // not doing receive silence detection
// every received packet counts
fEmpty = FALSE; } LeaveCriticalSection(&m_CritSect); return fEmpty; }
void RxStream::StartDecode() { MediaPacket *pAP; MMRESULT mmr;
// if we have a separate decode thread this will signal it.
// for now we insert the decode loop here
while (pAP = GetNextDecode()) { // if (pAP->Decode() != DPR_SUCCESS)
// {
// pAP->Recycle();
// }
mmr = m_pAudioFilter->Convert((AudioPacket *)pAP, AP_DECODE); if (mmr != MMSYSERR_NOERROR) { pAP->Recycle(); }
else { pAP->SetState(MP_STATE_DECODED);
if (m_dwFlags & DP_FLAG_AUTO_SILENCE_DETECT) { // dont play the packet if we have received at least a quarter second of silent packets.
// This will enable switch to talk (in half-duplex mode).
DWORD dw; pAP->GetSignalStrength(&dw); if (m_AudioMonitor.SilenceDetect((WORD)dw)) { m_SilenceDurationT += m_SamplesPerPkt; if (m_SilenceDurationT > m_SamplesPerSec/4) pAP->Recycle(); } else { m_SilenceDurationT = 0; } } } Release(pAP); } }
MediaPacket *RxStream::GetNextDecode(void) { MediaPacket *pAP = NULL; UINT pos; NETBUF *pBuf; EnterCriticalSection(&m_CritSect); // do we have any packets in the queue
if (! TS_EARLIER(m_MaxT , m_PlayT)) { pos = m_PlayPos; do { if (!m_Ring[pos]->Busy() && m_Ring[pos]->GetState() == MP_STATE_NET_IN_STREAM ) { if (m_pDecodeBufferPool) { // MediaPacket needs to be given a decode buffer
if ( pBuf = (NETBUF *)m_pDecodeBufferPool->GetBuffer()) { // init the buffer
pBuf->pool = m_pDecodeBufferPool; pBuf->length = m_pDecodeBufferPool->GetMaxBufferSize()-sizeof(NETBUF); pBuf->data = (PBYTE)(pBuf + 1); m_Ring[pos]->SetDecodeBuffer(pBuf); } else { break; // no buffers available
} } pAP = m_Ring[pos]; pAP->Busy(TRUE); break; } pos = ModRing(pos+1); } while (pos != ModRing(m_MaxPos+1)); } LeaveCriticalSection(&m_CritSect); return pAP; }
MediaPacket *RxStream::GetNextPlay(void) { MediaPacket *pAP = NULL; UINT pos; EnterCriticalSection(&m_CritSect);
pAP = m_Ring[m_PlayPos]; if (pAP->Busy() || (pAP->GetState() != MP_STATE_RESET && pAP->GetState() != MP_STATE_DECODED)) { // bad - the next packet is not decoded yet
pos = ModRing(m_FreePos-1); if (pos != m_PlayPos && !m_Ring[m_FreePos]->Busy() && m_Ring[m_FreePos]->GetState() == MP_STATE_RESET) { // give an empty buffer from the end
pAP = m_Ring[m_FreePos]; m_FreePos = pos; } else { // worse - no free packets
// this can only happen if packets are not released
// or we-re backed up all the way with new packets
// Reset?
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_RESET) { // give missing packets a timestamp
pAP->SetProp(MP_PROP_TIMESTAMP,m_PlayT); } pAP->Busy(TRUE); m_PlayPos = ModRing(m_PlayPos+1); m_PlayT += m_SamplesPerPkt;
// the worst hack in all of NAC.DLL - the injection of the
// DTMF "feedback tone". Clearly, this waveout stream stuff needs
// to be rewritten!
if (m_nBeeps > 0) { PVOID pBuffer=NULL; UINT uSize=0; WAVEFORMATEX wfx;
if ((pAP) && (m_dwFlags & DP_FLAG_AUDIO)) { pAP->GetDevData(&pBuffer, &uSize); if (pBuffer) { MakeDTMFBeep(&m_wfxDst, (PBYTE)pBuffer, uSize); pAP->SetState(MP_STATE_DECODED); pAP->SetRawActual(uSize); } }
m_nBeeps--; }
LeaveCriticalSection(&m_CritSect); return pAP; }
void RxStream::InjectBeeps(int nBeeps) { EnterCriticalSection(&m_CritSect);
m_nBeeps = nBeeps;
LeaveCriticalSection(&m_CritSect);
}
/*************************************************************************
Function: PeekPrevPlay(void)
Purpose : Get previous audio packet played back.
Returns : Pointer to that packet.
Params : None.
Comments:
History : Date Reason
06/02/96 Created - PhilF
*************************************************************************/ MediaPacket *RxStream::PeekPrevPlay(void) { MediaPacket *pAP = NULL; EnterCriticalSection(&m_CritSect);
// Get packet previously scheduled for playback from the ring
pAP = m_Ring[ModRing(m_PlayPos+m_RingSize-2)];
LeaveCriticalSection(&m_CritSect); return pAP; }
/*************************************************************************
Function: PeekNextPlay(void)
Purpose : Get next next audio packet to be played.
Returns : Pointer to that packet.
Params : None.
Comments:
History : Date Reason
06/02/96 Created - PhilF
*************************************************************************/ MediaPacket *RxStream::PeekNextPlay(void) { MediaPacket *pAP = NULL; EnterCriticalSection(&m_CritSect);
// Get packet next scheduled for playback from the ring
pAP = m_Ring[ModRing(m_PlayPos)];
LeaveCriticalSection(&m_CritSect); return pAP; }
HRESULT RxStream::GetSignalStrength(PDWORD pdw) { MediaPacket *pAP; EnterCriticalSection(&m_CritSect); pAP = m_Ring[m_PlayPos]; if (!pAP || pAP->Busy() || pAP->GetState() != MP_STATE_DECODED) *pdw = 0; else { pAP->GetSignalStrength(pdw); } LeaveCriticalSection(&m_CritSect); return DPR_SUCCESS; }
// Scan thru the ring, looking for the next
// decoded packet and report its RTP timestamp
BOOL RxStream::NextPlayablePacketTime(DWORD *pTS) { UINT pos; if (IsEmpty()) return FALSE; pos = m_PlayPos; do { if (m_Ring[pos]->Busy()) return FALSE; // no point scanning further
if (m_Ring[pos]->GetState() == MP_STATE_DECODED ) { *pTS = m_Ring[pos]->GetTimestamp(); return TRUE; } pos = ModRing(pos+1); } while (pos != ModRing(m_MaxPos+1)); // no decoded packets
return FALSE; }
void RxStream::Release(MediaPacket *pAP) { UINT pos; DWORD thisPos;
DWORD T; EnterCriticalSection(&m_CritSect); if (pAP->GetState() == MP_STATE_DECODED) { // if its playout time has pAPt reset it
T = pAP->GetTimestamp(); if (TS_EARLIER(T ,m_PlayT)) { pAP->MakeSilence(); } } pAP->Busy(FALSE); // Advance the free position if we are freeing the next one
pos = ModRing(m_FreePos+1); thisPos = pAP->GetIndex(); if (pos == thisPos) { // Releasing one packet may advance FreePos several
while (pos != m_PlayPos && !m_Ring[pos]->Busy()) { m_FreePos = pos; pos = ModRing(pos+1); } } LeaveCriticalSection(&m_CritSect); }
HRESULT RxStream::SetLastGoodSeq(UINT seq) { return DPR_SUCCESS; }
RxStream::Destroy(void) { UINT i; EnterCriticalSection(&m_CritSect); for (i=0; i < m_RingSize; i++) { if (m_Ring[i]) { m_Ring[i]->Release(); delete m_Ring[i]; m_Ring[i] = NULL; } } m_RingSize = 0;
if (m_pDecodeBufferPool) { delete m_pDecodeBufferPool; m_pDecodeBufferPool = NULL; } LeaveCriticalSection(&m_CritSect); return DPR_SUCCESS; }
|