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