Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

698 lines
18 KiB

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