|
|
#include "precomp.h"
#include "mixer.h"
#include "agc.h"
// #define LOGSTATISTICS_ON 1
DWORD SendAudioStream::RecordingThread () { HRESULT hr = DPR_SUCCESS; MediaPacket *pPacket; DWORD dwWait; HANDLE hEvent; DWORD dwDuplexType; DWORD dwVoiceSwitch; DWORD_PTR dwPropVal; DWORD dwSamplesPerPkt; DWORD dwSamplesPerSec; DWORD dwSilenceLimit, dwMaxStrength, dwLengthMS; WORD wPeakStrength; UINT u, uBufferSize; UINT uSilenceCount = 0; UINT uPrefeed = 0; UINT uTimeout = 0; DevMediaQueue dq; BOOL fSilent; AGC agc(NULL); // audio gain control object
CMixerDevice *pMixer = NULL; int nFailCount = 0; bool bCanSignalOpen=true; // should we signal that the device is open
// note: pMC is an artifact of when this thread was in the Datapump
// namespace. We can probably start phasing this variable out.
// in the mean time: "pMC = this" will suffice
// SendAudioStream *pMC = (SendAudioStream *)(m_pDP->m_Audio.pSendStream);
SendAudioStream *pMC = this;
ASSERT(pMC && (pMC->m_DPFlags & DPFLAG_INITIALIZED)); TxStream *pStream = pMC->m_SendStream; AcmFilter *pAudioFilter = pMC->m_pAudioFilter; // warning: typecasting a base class ptr to a derived class ptr.
WaveInControl *pMediaCtrl = (WaveInControl *)pMC->m_InMedia;
FX_ENTRY ("DP::RcrdTh:")
// get thread context
if (pStream == NULL || pAudioFilter == NULL || pMediaCtrl == NULL) { return DPR_INVALID_PARAMETER; }
// Enter critical section: QoS thread also reads the statistics
EnterCriticalSection(&pMC->m_crsQos);
// Initialize QoS structure
ZeroMemory(&pMC->m_Stats, 4UL * sizeof(DWORD));
// Initialize oldest QoS callback timestamp
pMC->m_Stats.dwNewestTs = pMC->m_Stats.dwOldestTs = timeGetTime();
// Leave critical section
LeaveCriticalSection(&pMC->m_crsQos);
pMediaCtrl->GetProp(MC_PROP_MEDIA_DEV_ID, &dwPropVal); if (dwPropVal != (DWORD)WAVE_MAPPER) { pMixer = CMixerDevice::GetMixerForWaveDevice(NULL, (DWORD)dwPropVal, MIXER_OBJECTF_WAVEIN); }
// even if pMixer is null, this is fine, AGC will catch subsequent errors
agc.SetMixer(pMixer);
// get thresholds
pMediaCtrl->GetProp (MC_PROP_TIMEOUT, &dwPropVal); uTimeout = (DWORD)dwPropVal; pMediaCtrl->GetProp (MC_PROP_PREFEED, &dwPropVal); uPrefeed = (DWORD)dwPropVal;
// get duplex type
pMediaCtrl->GetProp (MC_PROP_DUPLEX_TYPE, &dwPropVal); dwDuplexType = (DWORD)dwPropVal;
// get Samples/Pkt and Samples/Sec
pMediaCtrl->GetProp (MC_PROP_SPP, &dwPropVal); dwSamplesPerPkt = (DWORD)dwPropVal;
pMediaCtrl->GetProp (MC_PROP_SPS, &dwPropVal); dwSamplesPerSec = (DWORD)dwPropVal;
pMediaCtrl->GetProp (MC_PROP_SILENCE_DURATION, &dwPropVal); dwSilenceLimit = (DWORD)dwPropVal;
// calculate silence limit in units of packets
// silence_time_in_ms/packet_duration_in_ms
dwSilenceLimit = dwSilenceLimit*dwSamplesPerSec/(dwSamplesPerPkt*1000);
// length of a packet in millisecs
dwLengthMS = (dwSamplesPerPkt * 1000) / dwSamplesPerSec;
dq.SetSize (MAX_TXRING_SIZE);
WaitForSignal:
// DEBUGMSG (1, ("%s: WaitForSignal\r\n", _fx_));
{ pMediaCtrl->GetProp (MC_PROP_MEDIA_DEV_HANDLE, &dwPropVal); if (dwPropVal) { DEBUGMSG (ZONE_DP, ("%s: already open\r\n", _fx_)); goto SendLoop; // sound device already open
}
// in the full-duplex case, open and prepare the device and charge ahead.
// in the half duplex case wait for playback's signal before opening the device
while (TRUE) { // should I stop now???
if (pMC->m_ThreadFlags & DPTFLAG_STOP_RECORD) { DEBUGMSG (ZONE_DP, ("%s: STOP_1\r\n", _fx_)); goto MyEndThread; } dwWait = (dwDuplexType & DP_FLAG_HALF_DUPLEX) ? WaitForSingleObject (g_hEventHalfDuplex, uTimeout) : WAIT_OBJECT_0;
// now, let's check why I don't need to wait
if (dwWait == WAIT_OBJECT_0) { //DEBUGMSG (ZONE_DP, ("%s: try to open audio dev\r\n", _fx_));
LOG((LOGMSG_OPEN_AUDIO)); hr = pMediaCtrl->Open (); if (hr != DPR_SUCCESS) { DEBUGMSG (ZONE_DP, ("%s: MediaCtrl::Open failed, hr=0x%lX\r\n", _fx_, hr)); pMediaCtrl->SetProp(MC_PROP_AUDIO_JAMMED, TRUE);
SetEvent(g_hEventHalfDuplex);
nFailCount++;
if (nFailCount == MAX_FAILCOUNT) { // three attempts to open the device have failed
// signal to the UI that something is wrong
m_pDP->StreamEvent(MCF_SEND, MCF_AUDIO, STREAM_EVENT_DEVICE_FAILURE, 0); bCanSignalOpen = true; }
Sleep(2000); // Sleep for two seconds
continue; } // Notification is not used. if needed do it thru Channel
//pMC->m_Connection->DoNotification(CONNECTION_OPEN_MIC);
pMediaCtrl->PrepareHeaders (); goto SendLoop; }
} // while
}
SendLoop: nFailCount = 0;
pMediaCtrl->SetProp(MC_PROP_AUDIO_JAMMED, FALSE); if (bCanSignalOpen) { m_pDP->StreamEvent(MCF_SEND, MCF_AUDIO, STREAM_EVENT_DEVICE_OPEN, 0); bCanSignalOpen = false; // don't signal more than once per session
}
// DEBUGMSG (1, ("%s: SendLoop\r\n", _fx_));
// get event handle
pMediaCtrl->GetProp (MC_PROP_EVENT_HANDLE, &dwPropVal); hEvent = (HANDLE) dwPropVal; if (hEvent == NULL) { DEBUGMSG (ZONE_DP, ("%s: invalid event\r\n", _fx_)); return DPR_CANT_CREATE_EVENT; }
// hey, in the very beginning, let's 'Start' it
hr = pMediaCtrl->Start (); if (hr != DPR_SUCCESS) { DEBUGMSG (ZONE_DP, ("%s: MediaControl::Start failed, hr=0x%lX\r\n", _fx_, hr)); goto MyEndThread; }
// update timestamp to account for the 'sleep' period
pMC->m_SendTimestamp += (GetTickCount() - pMC->m_SavedTickCount)*dwSamplesPerSec/1000;
// let's feed four buffers first
for (u = 0; u < uPrefeed; u++) { if ((pPacket = pStream->GetFree ()) != NULL) { if ((hr = pPacket->Record ()) != DPR_SUCCESS) { DEBUGMSG (ZONE_DP, ("%s: Record failed, hr=0x%lX\r\n", _fx_, hr)); } dq.Put (pPacket); } }
// let's get into the loop, mm system notification loop
pMC->m_fSending= FALSE; while (TRUE) { dwWait = WaitForSingleObject (hEvent, uTimeout);
// should I stop now???
if (pMC->m_ThreadFlags & DPTFLAG_STOP_RECORD) { DEBUGMSG (ZONE_DP, ("%s: STOP_3\r\n", _fx_)); goto HalfDuplexYield; } // get current voice switching mode
pMediaCtrl->GetProp (MC_PROP_VOICE_SWITCH, &dwPropVal); dwVoiceSwitch = (DWORD)dwPropVal;
// see why I don't need to wait
if (dwWait != WAIT_TIMEOUT) { while (TRUE) { if ((pPacket = dq.Peek ()) != NULL) { if (! pPacket->IsBufferDone ()) { break; } else { if (pMC->m_mmioSrc.fPlayFromFile && pMC->m_mmioSrc.hmmioSrc) pPacket->ReadFromFile (&pMC->m_mmioSrc); u--; // one less buffer with the wave device
} } else { DEBUGMSG (ZONE_VERBOSE, ("%s: Peek is NULL\r\n", _fx_)); break; }
pPacket = dq.Get ();
((AudioPacket*)pPacket)->ComputePower (&dwMaxStrength, &wPeakStrength);
// is this packet silent?
fSilent = pMC->m_AudioMonitor.SilenceDetect((WORD)dwMaxStrength); if((dwVoiceSwitch == DP_FLAG_AUTO_SWITCH) && fSilent) { // pPacket->SetState (MP_STATE_RESET); // note: done in Recycle
if (++uSilenceCount >= dwSilenceLimit) { pMC->m_fSending = FALSE; // stop sending packets
// if half duplex mode and playback thread may be waiting
if (dwDuplexType & DP_FLAG_HALF_DUPLEX) { IMediaChannel *pIMC = NULL; RecvMediaStream *pRecv; m_pDP->GetMediaChannelInterface(MCF_RECV | MCF_AUDIO, &pIMC); if (pIMC) { pRecv = static_cast<RecvMediaStream *> (pIMC); if (pRecv->IsEmpty()==FALSE) { //DEBUGMSG (ZONE_DP, ("%s: too many silence and Yield\r\n", _fx_));
LOG((LOGMSG_REC_YIELD)); pPacket->Recycle (); pStream->PutNextRecorded (pPacket); uSilenceCount = 0; pIMC->Release(); goto HalfDuplexYield; } pIMC->Release(); } } } } else { switch(dwVoiceSwitch) { // either there was NO silence, or manual switching is in effect
default: case DP_FLAG_AUTO_SWITCH: // this proves no silence (in this path because of non-silence)
case DP_FLAG_MIC_ON: pMC->m_fSending = TRUE; uSilenceCount = 0; break; case DP_FLAG_MIC_OFF: pMC->m_fSending = FALSE; break; }
} if (pMC->m_fSending) { pPacket->SetState (MP_STATE_RECORDED);
// do AUTOMIX, but ignore DTMF tones
if (pMC->m_bAutoMix) { agc.Update(wPeakStrength, dwLengthMS); } } else { pPacket->Recycle();
// Enter critical section: QoS thread also reads the statistics
EnterCriticalSection(&pMC->m_crsQos);
// Update total number of packets recorded
pMC->m_Stats.dwCount++;
// Leave critical section
LeaveCriticalSection(&pMC->m_crsQos); }
pPacket->SetProp(MP_PROP_TIMESTAMP,pMC->m_SendTimestamp); // pPacket->SetProp(MP_PROP_TIMESTAMP,GetTickCount());
pMC->m_SendTimestamp += dwSamplesPerPkt; pStream->PutNextRecorded (pPacket);
} // while
} else { if (dwDuplexType & DP_FLAG_HALF_DUPLEX) { DEBUGMSG (ZONE_DP, ("%s: Timeout and Yield\r\n", _fx_)); goto HalfDuplexYield; } } // if
pMC->Send();
// Make sure the recorder has an adequate number of buffers
while ((pPacket = pStream->GetFree()) != NULL) { if ((hr = pPacket->Record ()) == DPR_SUCCESS) { dq.Put (pPacket); } else { dq.Put (pPacket); DEBUGMSG (ZONE_DP, ("%s: Record FAILED, hr=0x%lX\r\n", _fx_, hr)); break; } u++; } if (u < uPrefeed) { DEBUGMSG (ZONE_DP, ("%s: NO FREE BUFFERS\r\n", _fx_)); } } // while TRUE
goto MyEndThread;
HalfDuplexYield:
// stop and reset audio device
pMediaCtrl->Reset ();
// flush dq
while ((pPacket = dq.Get ()) != NULL) { pStream->PutNextRecorded (pPacket); pPacket->Recycle (); }
// save real time so we can update the timestamp when we restart
pMC->m_SavedTickCount = GetTickCount();
// reset the event
ResetEvent (hEvent);
// close audio device
pMediaCtrl->UnprepareHeaders (); pMediaCtrl->Close ();
// signal playback thread to start
SetEvent (g_hEventHalfDuplex);
if (!(pMC->m_ThreadFlags & DPTFLAG_STOP_RECORD)) {
// yield
// playback has to claim the device within 100ms or we take it back.
Sleep (100);
// wait for playback's signal
goto WaitForSignal; }
MyEndThread:
if (pMixer) delete pMixer;
pMediaCtrl->SetProp(MC_PROP_AUDIO_JAMMED, FALSE);
pMC->m_fSending = FALSE; DEBUGMSG (ZONE_DP, ("%s: Exiting.\r\n", _fx_)); return hr; }
DWORD RecvAudioStream::PlaybackThread ( void) { HRESULT hr = DPR_SUCCESS; MediaPacket * pPacket; MediaPacket * pPrevPacket; MediaPacket * pNextPacket; DWORD dwWait; HANDLE hEvent; DWORD dwDuplexType; DWORD_PTR dwPropVal; UINT u; UINT uMissingCount = 0; UINT uPrefeed = 0; UINT uTimeout = 0; UINT uSamplesPerPkt=0; DevMediaQueue dq; UINT uGoodPacketsQueued = 0; int nFailCount = 0; bool bCanSignalOpen=true; //warning: casting from base to dervied class
// note: pMC is an artifact of when this thread was in the Datapump
// namespace. We can probably start phasing this variable out.
// in the mean time: "pMC = this" will suffice
// RecvAudioStream *pMC = (RecvAudioStream *)(m_pDP->m_Audio.pRecvStream);
RecvAudioStream *pMC = this; RxStream *pStream = pMC->m_RecvStream; MediaControl *pMediaCtrl = pMC->m_OutMedia;
#if 0
NETBUF * pStaticNetBuf; #endif
FX_ENTRY ("DP::PlayTh")
if (pStream == NULL || m_pAudioFilter == NULL || pMediaCtrl == NULL) { return DPR_INVALID_PARAMETER; }
// get event handle
pMediaCtrl->GetProp (MC_PROP_EVENT_HANDLE, &dwPropVal); hEvent = (HANDLE) dwPropVal; if (hEvent == NULL) { DEBUGMSG (ZONE_DP, ("%s: invalid event\r\n", _fx_)); return DPR_CANT_CREATE_EVENT; }
// get thresholds
pMediaCtrl->GetProp (MC_PROP_TIMEOUT, &dwPropVal); uTimeout = (DWORD)dwPropVal;
uPrefeed = pStream->BufferDelay();
// get samples per pkt
pMediaCtrl->GetProp(MC_PROP_SPP, &dwPropVal); uSamplesPerPkt = (DWORD)dwPropVal; // get duplex type
pMediaCtrl->GetProp (MC_PROP_DUPLEX_TYPE, &dwPropVal); dwDuplexType = (DWORD)dwPropVal;
// set dq size
dq.SetSize (uPrefeed);
WaitForSignal:
// DEBUGMSG (1, ("%s: WaitForSignal\r\n", _fx_));
pMediaCtrl->GetProp (MC_PROP_MEDIA_DEV_HANDLE, &dwPropVal); if (dwPropVal) { DEBUGMSG (ZONE_DP, ("%s: already open\r\n", _fx_)); goto RecvLoop; // already open
}
// in the full-duplex case, open and prepare the device and charge ahead.
// in the half duplex case wait for playback's signal before opening the device
while (TRUE) { // should I stop now???
if (pMC->m_ThreadFlags & DPTFLAG_STOP_PLAY) { DEBUGMSG (ZONE_VERBOSE, ("%s: STOP_1\r\n", _fx_)); goto MyEndThread; } dwWait = (dwDuplexType & DP_FLAG_HALF_DUPLEX) ? WaitForSingleObject (g_hEventHalfDuplex, uTimeout) : WAIT_OBJECT_0;
// to see why I don't need to wait
if (dwWait == WAIT_OBJECT_0) { // DEBUGMSG (1, ("%s: try to open audio dev\r\n", _fx_));
pStream->FastForward(FALSE); // GJ - flush receive queue
hr = pMediaCtrl->Open (); if (hr != DPR_SUCCESS) { // somebody may have commandeered the wave out device
// this could be a temporary problem so lets give it some time
DEBUGMSG (ZONE_DP, ("%s: MediaControl::Open failed, hr=0x%lX\r\n", _fx_, hr)); pMediaCtrl->SetProp(MC_PROP_AUDIO_JAMMED, TRUE);
SetEvent(g_hEventHalfDuplex);
nFailCount++;
if (nFailCount == MAX_FAILCOUNT) { // three attempts to open the device have failed
// signal to the UI that something is wrong
m_pDP->StreamEvent(MCF_RECV, MCF_AUDIO, STREAM_EVENT_DEVICE_FAILURE, 0); bCanSignalOpen = true; }
Sleep(2000); // sleep for two seconds
continue; } // Notification is not used. if needed do it thru Channel
//pMC->m_Connection->DoNotification(CONNECTION_OPEN_SPK);
pMediaCtrl->PrepareHeaders ();
goto RecvLoop; } } // while
RecvLoop: nFailCount = 0; pMediaCtrl->SetProp(MC_PROP_AUDIO_JAMMED, FALSE); if (bCanSignalOpen) { m_pDP->StreamEvent(MCF_RECV, MCF_AUDIO, STREAM_EVENT_DEVICE_OPEN, 0); bCanSignalOpen = false; // don't signal open more than once per session
}
// Set my thread priority high
// This thread doesnt do any compute intensive work (except maybe
// interpolate?).
// Its sole purpose is to stream ready buffers to the sound device
SetThreadPriority(pMC->m_hRenderingThread, THREAD_PRIORITY_HIGHEST); // DEBUGMSG (1, ("%s: SendLoop\r\n", _fx_));
// let's feed four buffers first
// But make sure the receive stream has enough buffering delay
// so we dont read past the last packet.
//if (uPrefeed > pStream->BufferDelay())
uGoodPacketsQueued = 0; for (u = 0; u < uPrefeed; u++) { if ((pPacket = pStream->GetNextPlay ()) != NULL) { if (pPacket->GetState () == MP_STATE_RESET) { // hr = pPacket->Play (pStaticNetBuf);
hr = pPacket->Play (&pMC->m_mmioDest, MP_DATATYPE_SILENCE); } else { // hr = pPacket->Play ();
hr = pPacket->Play (&pMC->m_mmioDest, MP_DATATYPE_FROMWIRE); uGoodPacketsQueued++; }
if (hr != DPR_SUCCESS) { DEBUGMSG (ZONE_DP, ("%s: Play failed, hr=0x%lX\r\n", _fx_, hr)); SetEvent(hEvent); }
dq.Put (pPacket); } }
pMC->m_fReceiving = TRUE; // let's get into the loop
uMissingCount = 0; while (TRUE) { dwWait = WaitForSingleObject (hEvent, uTimeout);
// should I stop now???
if (pMC->m_ThreadFlags & DPTFLAG_STOP_PLAY) { DEBUGMSG (ZONE_VERBOSE, ("%s: STOP_3\r\n", _fx_)); goto HalfDuplexYield; }
// see why I don't need to wait
if (dwWait != WAIT_TIMEOUT) { while (TRUE) { if ((pPacket = dq.Peek ()) != NULL) { if (! pPacket->IsBufferDone ()) { break; } } else { DEBUGMSG (ZONE_VERBOSE, ("%s: Peek is NULL\r\n", _fx_)); break; }
pPacket = dq.Get (); if (pPacket->GetState() != MP_STATE_PLAYING_SILENCE) uGoodPacketsQueued--; // a non-empty buffer just got done
pMC->m_PlaybackTimestamp = pPacket->GetTimestamp() + uSamplesPerPkt; pPacket->Recycle (); pStream->Release (pPacket);
if ((pPacket = pStream->GetNextPlay ()) != NULL) { // check if we are in half-duplex mode and also if
// the recording thread is around.
if (dwDuplexType & DP_FLAG_HALF_DUPLEX) { IMediaChannel *pIMC = NULL; BOOL fSending = FALSE; m_pDP->GetMediaChannelInterface(MCF_SEND | MCF_AUDIO, &pIMC); if (pIMC) { fSending = (pIMC->GetState() == MSSTATE_STARTED); pIMC->Release(); } if (fSending) { if (pPacket->GetState () == MP_STATE_RESET) { // Decide if its time to yield
// Dont want to yield until we've finished playing all data packets
//
if (!uGoodPacketsQueued && (pStream->IsEmpty() || ++uMissingCount >= DEF_MISSING_LIMIT)) { //DEBUGMSG (ZONE_DP, ("%s: too many missings and Yield\r\n", _fx_));
LOG( (LOGMSG_PLAY_YIELD)); pPacket->Recycle (); pStream->Release (pPacket); goto HalfDuplexYield; } } else { uMissingCount = 0; } } }
if (pPacket->GetState () == MP_STATE_RESET) { pPrevPacket = pStream->PeekPrevPlay (); pNextPacket = pStream->PeekNextPlay (); hr = pPacket->Interpolate(pPrevPacket, pNextPacket); if (hr != DPR_SUCCESS) { //DEBUGMSG (ZONE_DP, ("%s: Interpolate failed, hr=0x%lX\r\n", _fx_, hr));
hr = pPacket->Play (&pMC->m_mmioDest, MP_DATATYPE_SILENCE); } else hr = pPacket->Play (&pMC->m_mmioDest, MP_DATATYPE_INTERPOLATED); } else { // hr = pPacket->Play ();
hr = pPacket->Play (&pMC->m_mmioDest, MP_DATATYPE_FROMWIRE); uGoodPacketsQueued++; }
if (hr != DPR_SUCCESS) { DEBUGMSG (ZONE_DP, ("%s: Play failed, hr=0x%lX\r\n", _fx_, hr)); SetEvent(hEvent); }
dq.Put (pPacket); } else { DEBUGMSG( ZONE_DP, ("%s: NO PLAY BUFFERS!",_fx_)); } } // while
} else { if (dwDuplexType & DP_FLAG_HALF_DUPLEX) { DEBUGMSG (ZONE_DP, ("%s: Timeout and Yield!\r\n", _fx_)); goto HalfDuplexYield; } } } // while TRUE
goto MyEndThread;
HalfDuplexYield:
pMC->m_fReceiving = FALSE; // stop and reset audio device
pMediaCtrl->Reset ();
// flush dq
while ((pPacket = dq.Get ()) != NULL) { pPacket->Recycle (); pStream->Release (pPacket); }
// reset the event
ResetEvent (hEvent);
// close audio device
pMediaCtrl->UnprepareHeaders (); pMediaCtrl->Close ();
// signal recording thread to start
SetEvent (g_hEventHalfDuplex);
if (!(pMC->m_ThreadFlags & DPTFLAG_STOP_PLAY)) { // yield
Sleep (0);
// wait for recording's signal
// restore thread priority
SetThreadPriority(pMC->m_hRenderingThread,THREAD_PRIORITY_NORMAL); goto WaitForSignal; }
MyEndThread:
pMediaCtrl->SetProp(MC_PROP_AUDIO_JAMMED, FALSE);
DEBUGMSG(ZONE_DP, ("%s: Exiting.\n", _fx_)); return hr; }
DWORD SendAudioStream::Send()
{ MMRESULT mmr; MediaPacket *pAP; void *pBuffer; DWORD dwBeforeEncode; DWORD dwAfterEncode; DWORD dwPacketSize; UINT uBytesSent; #ifdef LOGSTATISTICS_ON
char szDebug[256]; DWORD dwDebugSaveBits; #endif
while ( pAP = m_SendStream->GetNext()) { if (!(m_ThreadFlags & DPTFLAG_PAUSE_SEND)) {
dwBeforeEncode = timeGetTime(); mmr = m_pAudioFilter->Convert((AudioPacket*)pAP, AP_ENCODE); if (mmr == MMSYSERR_NOERROR) { pAP->SetState(MP_STATE_ENCODED); }
// Time the encoding operation
dwAfterEncode = timeGetTime() - dwBeforeEncode;
if (mmr == MMSYSERR_NOERROR) { SendPacket((AudioPacket*)pAP, &uBytesSent); } else { uBytesSent = 0; }
UPDATE_COUNTER(g_pctrAudioSendBytes, uBytesSent*8);
// Enter critical section: QoS thread also reads the statistics
EnterCriticalSection(&m_crsQos);
// Update total number of packets recorded
m_Stats.dwCount++;
// Save the perfs in our stats structure for QoS
#ifdef LOGSTATISTICS_ON
dwDebugSaveBits = m_Stats.dwBits; #endif
// Add this new frame size to the cumulated size
m_Stats.dwBits += (uBytesSent * 8);
// Add this compression time to total compression time
m_Stats.dwMsComp += dwAfterEncode;
#ifdef LOGSTATISTICS_ON
wsprintf(szDebug, " A: (Voiced) dwBits = %ld up from %ld (file: %s line: %ld)\r\n", m_Stats.dwBits, dwDebugSaveBits, __FILE__, __LINE__); OutputDebugString(szDebug); #endif
// Leave critical section
LeaveCriticalSection(&m_crsQos); }
// whether or not we sent the packet, we need to return
// it to the free queue
pAP->m_fMark=0; pAP->SetState(MP_STATE_RESET); m_SendStream->Release(pAP); } return DPR_SUCCESS; }
// queues and sends the packet
// if the packet failed the encode process, it doesn't get sent
HRESULT SendAudioStream::SendPacket(AudioPacket *pAP, UINT *puBytesSent) { PS_QUEUE_ELEMENT psq; UINT uLength; int nPacketsSent=0;
if (pAP->GetState() != MP_STATE_ENCODED) { DEBUGMSG (ZONE_ACM, ("SendAudioStream::SendPacket: Packet not compressed\r\n")); *puBytesSent = 0; return E_FAIL; }
ASSERT(m_pRTPSend);
psq.pMP = pAP; psq.dwPacketType = PS_AUDIO; psq.pRTPSend = m_pRTPSend; pAP->GetNetData((void**)(&(psq.data)), &uLength); ASSERT(psq.data); psq.dwSize = uLength; psq.fMark = pAP->m_fMark; psq.pHeaderInfo = NULL; psq.dwHdrSize = 0;
*puBytesSent = uLength + sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE;
// add audio packets to the front of the queue
m_pDP->m_PacketSender.m_SendQueue.PushFront(psq);
while (m_pDP->m_PacketSender.SendPacket()) { ; }
return S_OK;
};
#ifdef OLDSTUFF
/*
// Winsock 1 receive thread
// Creates a hidden window and a message loop to process WINSOCK window
// messages. Also processes private messages from the datapump to start/stop
// receiving on a particular media stream
*/ DWORD DataPump::CommonRecvThread (void ) {
HRESULT hr; HWND hWnd = (HWND)NULL; RecvMediaStream *pRecvMC; BOOL fChange = FALSE; MSG msg; DWORD curTime, nextUpdateTime = 0, t; UINT timerId = 0; FX_ENTRY ("DP::RecvTh")
// Create hidden window
hWnd = CreateWindowEx( WS_EX_NOPARENTNOTIFY, "SockMgrWClass", /* See RegisterClass() call. */ NULL, WS_CHILD , /* Window style. */ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hAppWnd, /* the application window is the parent. */ (HMENU)this, /* hardcoded ID */ m_hAppInst, /* the application owns this window. */ NULL /* Pointer not needed. */ );
if(!hWnd) { hr = GetLastError(); DEBUGMSG(ZONE_DP,("CreateWindow returned %d\n",hr)); goto CLEANUPEXIT; } SetThreadPriority(m_hRecvThread, THREAD_PRIORITY_ABOVE_NORMAL);
// This function is guaranteed to create a queue on this thread
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
// notify thread creator that we're ready to recv messages
SetEvent(m_hRecvThreadAckEvent);
// Wait for control messages from Start()/Stop() or Winsock messages directed to
// our hidden window
while (GetMessage(&msg, NULL, 0, 0)) { switch(msg.message) { case MSG_START_RECV: // Start receiving on the specified media stream
DEBUGMSG(ZONE_VERBOSE,("%s: MSG_START_RECV\n",_fx_)); pRecvMC = (RecvMediaStream *)msg.lParam; // call the stream to post recv buffers and
// tell Winsock to start sending socket msgs to our window
pRecvMC->StartRecv(hWnd); fChange = TRUE; break; case MSG_STOP_RECV: // Stop receiving on the specified media stream
DEBUGMSG(ZONE_VERBOSE,("%s: MSG_STOP_RECV\n",_fx_)); pRecvMC = (RecvMediaStream *)msg.lParam; // call the stream to cancel outstanding recvs etc.
// currently we assume this can be done synchronously
pRecvMC->StopRecv(); fChange = TRUE; break; case MSG_EXIT_RECV: // Exit the recv thread.
// Assume that we are not currently receving on any stream.
DEBUGMSG(ZONE_VERBOSE,("%s: MSG_EXIT_RECV\n",_fx_)); fChange = TRUE; if (DestroyWindow(hWnd)) { break; } DEBUGMSG(ZONE_DP,("DestroyWindow returned %d\n",GetLastError())); // fall thru to PostQuitMessage()
case WM_DESTROY: PostQuitMessage(0); break; case WM_TIMER: if (msg.hwnd == NULL) { // this timer is for the benefit of ThreadTimer::UpdateTime()
// however, we are calling UpdateTime after every message (see below)
// so we dont do anything special here.
break; } default: TranslateMessage(&msg); DispatchMessage(&msg); }
if (fChange) { // the thread MSGs need to be acked
SetEvent(m_hRecvThreadAckEvent); fChange = FALSE; } t = m_RecvTimer.UpdateTime(curTime=GetTickCount()); if (t != nextUpdateTime) { // Thread timer wants to change its update time
nextUpdateTime = t; if (timerId) { KillTimer(NULL,timerId); timerId = 0; } // if nextTime is zero, there are no scheduled timeouts so we dont need to call UpdateTime
if (nextUpdateTime) timerId = SetTimer(NULL, 0, nextUpdateTime - curTime + 50, NULL); }
}
CLEANUPEXIT: DEBUGMSG(ZONE_DP,("%s terminating.\n", _fx_));
return hr;
}
#endif
/*
Winsock 2 receive thread. Main differnce here is that it has a WaitEx loop where we wait for Start/Stop commands from the datapump while allowing WS2 APCs to be handled. Note: Only way to use the same thread routine for WS1 and WS2 is with MsgWaitForMultipleObjectsEx, which unfortunately is not implemented in Win95 */ DWORD DataPump::CommonWS2RecvThread (void ) {
HRESULT hr; RecvMediaStream *pRecvMC; BOOL fChange = FALSE, fExit = FALSE; DWORD dwWaitStatus; DWORD curTime, t; FX_ENTRY ("DP::WS2RecvTh")
SetThreadPriority(m_hRecvThread, THREAD_PRIORITY_ABOVE_NORMAL);
// notify thread creator that we're ready to recv messages
SetEvent(m_hRecvThreadAckEvent);
while (!fExit) { // Wait for control messages from Start()/Stop() or Winsock async
// thread callbacks
// dispatch expired timeouts and check how long we need to wait
t = m_RecvTimer.UpdateTime(curTime=GetTickCount()); t = (t ? t-curTime+50 : INFINITE); dwWaitStatus = WaitForSingleObjectEx(m_hRecvThreadSignalEvent,t,TRUE); if (dwWaitStatus == WAIT_OBJECT_0) { switch(m_CurRecvMsg) { case MSG_START_RECV: // Start receiving on the specified media stream
DEBUGMSG(ZONE_VERBOSE,("%s: MSG_START_RECV\n",_fx_)); pRecvMC = m_pCurRecvStream; // call the stream to post recv buffers and
// tell Winsock to start sending socket msgs to our window
pRecvMC->StartRecv(NULL); fChange = TRUE; break; case MSG_STOP_RECV: // Stop receiving on the specified media stream
DEBUGMSG(ZONE_VERBOSE,("%s: MSG_STOP_RECV\n",_fx_)); pRecvMC = m_pCurRecvStream; // call the stream to cancel outstanding recvs etc.
// currently we assume this can be done synchronously
pRecvMC->StopRecv(); fChange = TRUE; break; case MSG_EXIT_RECV: // Exit the recv thread.
// Assume that we are not currently receving on any stream.
DEBUGMSG(ZONE_VERBOSE,("%s: MSG_EXIT_RECV\n",_fx_)); fChange = TRUE; fExit = TRUE; break;
case MSG_PLAY_SOUND: fChange = TRUE; pRecvMC->OnDTMFBeep(); break; default: // shouldnt be anything else
ASSERT(0); }
if (fChange) { // the thread MSGs need to be acked
SetEvent(m_hRecvThreadAckEvent); fChange = FALSE; }
} else if (dwWaitStatus == WAIT_IO_COMPLETION) { // nothing to do here
} else if (dwWaitStatus != WAIT_TIMEOUT) { DEBUGMSG(ZONE_DP,("%s: Wait failed with %d",_fx_,GetLastError())); fExit=TRUE; } }
DEBUGMSG(ZONE_DP,("%s terminating.\n", _fx_));
return 0;
}
void ThreadTimer::SetTimeout(TTimeout *pTObj) { DWORD time = pTObj->GetDueTime(); // insert in increasing order of timeout
for (TTimeout *pT = m_TimeoutList.pNext; pT != &m_TimeoutList; pT = pT->pNext) { if ((int)(pT->m_DueTime- m_CurTime) > (int) (time - m_CurTime)) break; } pTObj->InsertAfter(pT->pPrev); }
void ThreadTimer::CancelTimeout(TTimeout *pTObj) { pTObj->Remove(); // remove from list
}
// Called by thread with the current time as input (usually obtained from GetTickCount())
// Returns the time by which UpdateTime() should be called again or currentTime+0xFFFFFFFF if there
// are no scheduled timeouts
DWORD ThreadTimer::UpdateTime(DWORD curTime) { TTimeout *pT; m_CurTime = curTime; // figure out which timeouts have elapsed and do the callbacks
while (!IsEmpty()) { pT = m_TimeoutList.pNext; if ((int)(pT->m_DueTime-m_CurTime) <= 0) { pT->Remove(); pT->TimeoutIndication(); } else break; } return (IsEmpty() ? m_CurTime+INFINITE : m_TimeoutList.pNext->m_DueTime); }
|