#include "precomp.h" #ifdef PLS_DEBUG #include "plog.h" extern CPacketLog *g_pPacketLog; #endif // #define LOGSTATISTICS_ON 1 UINT g_MinWaveAudioDelayMs=240; // minimum millisecs of introduced playback delay (Wave) UINT g_MaxAudioDelayMs=750; // maximum milliesecs of introduced playback delay UINT g_MinDSEmulAudioDelayMs=240; // minimum delay (DirectSound on emulated driver) HRESULT STDMETHODCALLTYPE RecvAudioStream::QueryInterface(REFIID iid, void **ppVoid) { // resolve duplicate inheritance to the RecvMediaStream; extern IID IID_IProperty; if (iid == IID_IUnknown) { *ppVoid = (IUnknown*)((RecvMediaStream*)this); } else if (iid == IID_IMediaChannel) { *ppVoid = (IMediaChannel*)((RecvMediaStream *)this); } else if (iid == IID_IAudioChannel) { *ppVoid = (IAudioChannel*)this; } else if (iid == IID_IProperty) { *ppVoid = NULL; ERROR_OUT(("Don't QueryInterface for IID_IProperty, use IMediaChannel")); return E_NOINTERFACE; } else { *ppVoid = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE RecvAudioStream::AddRef(void) { return InterlockedIncrement(&m_lRefCount); } ULONG STDMETHODCALLTYPE RecvAudioStream::Release(void) { LONG lRet; lRet = InterlockedDecrement(&m_lRefCount); if (lRet == 0) { delete this; return 0; } else return lRet; } HRESULT RecvAudioStream::Initialize( DataPump *pDP) { HRESULT hr = DPR_OUT_OF_MEMORY; DWORD dwFlags = DP_FLAG_FULL_DUPLEX | DP_FLAG_AUTO_SWITCH ; MEDIACTRLINIT mcInit; FX_ENTRY ("RecvAudioStream::Initialize") InitializeCriticalSection(&m_crsAudQoS); dwFlags |= DP_FLAG_ACM | DP_FLAG_MMSYSTEM | DP_FLAG_AUTO_SILENCE_DETECT; // store the platform flags // enable Send and Recv by default m_DPFlags = (dwFlags & DP_MASK_PLATFORM) | DPFLAG_ENABLE_SEND | DPFLAG_ENABLE_RECV; // store a back pointer to the datapump container m_pDP = pDP; m_pIRTPRecv = NULL; m_Net = NULL; // this object (m_Net) no longer used (at least for now) m_dwSrcSize = 0; // Initialize data (should be in constructor) m_RenderingDevice = (UINT) -1; // use VIDEO_MAPPER // Create Receive and Transmit audio streams DBG_SAVE_FILE_LINE m_RecvStream = new RxStream(MAX_RXRING_SIZE); if (!m_RecvStream ) { DEBUGMSG (ZONE_DP, ("%s: RxStream or TxStream new failed\r\n", _fx_)); goto StreamAllocError; } // Create Input and Output audio filters DBG_SAVE_FILE_LINE m_pAudioFilter = new AcmFilter(); if (!m_pAudioFilter) { DEBUGMSG (ZONE_DP, ("%s: AcmManager new failed\r\n", _fx_)); goto FilterAllocError; } //Create MultiMedia device control objects DBG_SAVE_FILE_LINE m_OutMedia = new WaveOutControl(); if ( !m_OutMedia) { DEBUGMSG (ZONE_DP, ("%s: MediaControl new failed\r\n", _fx_)); goto MediaAllocError; } // Initialize the recv-stream media control object mcInit.dwFlags = dwFlags | DP_FLAG_RECV; hr = m_OutMedia->Initialize(&mcInit); if (hr != DPR_SUCCESS) { DEBUGMSG (ZONE_DP, ("%s: OMedia->Init failed, hr=0x%lX\r\n", _fx_, hr)); goto MediaAllocError; } // determine if the wave devices are available if (waveOutGetNumDevs()) m_DPFlags |= DP_FLAG_PLAY_CAP; // set media to half duplex mode by default m_OutMedia->SetProp(MC_PROP_DUPLEX_TYPE, DP_FLAG_HALF_DUPLEX); m_DPFlags |= DPFLAG_INITIALIZED; UPDATE_REPORT_ENTRY(g_prptSystemSettings, 0, REP_SYS_AUDIO_DSOUND); RETAILMSG(("NAC: Audio Subsystem: WAVE")); return DPR_SUCCESS; MediaAllocError: if (m_OutMedia) delete m_OutMedia; FilterAllocError: if (m_pAudioFilter) delete m_pAudioFilter; StreamAllocError: if (m_RecvStream) delete m_RecvStream; ERRORMESSAGE( ("%s: exit, hr=0x%lX\r\n", _fx_, hr)); return hr; } RecvAudioStream::~RecvAudioStream() { if (m_DPFlags & DPFLAG_INITIALIZED) { m_DPFlags &= ~DPFLAG_INITIALIZED; if (m_DPFlags & DPFLAG_CONFIGURED_RECV) UnConfigure(); if (m_pIRTPRecv) { m_pIRTPRecv->Release(); m_pIRTPRecv = NULL; } // Close the receive and transmit streams if (m_RecvStream) delete m_RecvStream; // Close the wave devices if (m_OutMedia) { delete m_OutMedia;} // close the filter if (m_pAudioFilter) delete m_pAudioFilter; m_pDP->RemoveMediaChannel(MCF_RECV|MCF_AUDIO, (IMediaChannel*)(RecvMediaStream*)this); } DeleteCriticalSection(&m_crsAudQoS); } HRESULT STDMETHODCALLTYPE RecvAudioStream::Configure( BYTE *pFormat, UINT cbFormat, BYTE *pChannelParams, UINT cbParams, IUnknown *pUnknown) { HRESULT hr; BOOL fRet; MEDIAPACKETINIT apInit; MEDIACTRLCONFIG mcConfig; MediaPacket **ppAudPckt; ULONG cAudPckt; DWORD_PTR dwPropVal; DWORD dwFlags; AUDIO_CHANNEL_PARAMETERS audChannelParams; UINT uAudioCodec; UINT ringSize = MAX_RXRING_SIZE; WAVEFORMATEX *pwfRecv; UINT maxRingSamples; MMRESULT mmr; FX_ENTRY ("RecvAudioStream::Configure") if (m_DPFlags & DPFLAG_STARTED_RECV) { return DPR_IO_PENDING; // anything better to return } if (m_DPFlags & DPFLAG_CONFIGURED_RECV) { DEBUGMSG(ZONE_DP, ("Stream Re-Configuration - calling UnConfigure")); UnConfigure(); } // get format details if ((NULL == pFormat) || (NULL == pChannelParams) || (cbFormat < sizeof(WAVEFORMATEX)) ) { return DPR_INVALID_PARAMETER; } audChannelParams = *(AUDIO_CHANNEL_PARAMETERS *)pChannelParams; pwfRecv = (WAVEFORMATEX *)pFormat; if (! (m_DPFlags & DPFLAG_INITIALIZED)) return DPR_OUT_OF_MEMORY; //BUGBUG: return proper error; // full or half duplex ? get flags from media control - use the record side hr = m_OutMedia->GetProp(MC_PROP_DUPLEX_TYPE, &dwPropVal); dwFlags = (DWORD)dwPropVal; if(!HR_SUCCEEDED(hr)) { dwFlags = DP_FLAG_HALF_DUPLEX | DP_FLAG_AUTO_SWITCH; } // if (m_Net) // { // hr = m_Net->QueryInterface(IID_IRTPRecv, (void **)&m_pIRTPRecv); // if (!SUCCEEDED(hr)) // return hr; // } mcConfig.uDuration = MC_USING_DEFAULT; // set duration by samples per pkt mmr = AcmFilter::SuggestDecodeFormat(pwfRecv, &m_fDevRecv); UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfRecv->wFormatTag, REP_RECV_AUDIO_FORMAT); UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfRecv->nSamplesPerSec, REP_RECV_AUDIO_SAMPLING); UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfRecv->nAvgBytesPerSec*8, REP_RECV_AUDIO_BITRATE); RETAILMSG(("NAC: Audio Recv Format: %s", (pwfRecv->wFormatTag == 66) ? "G723.1" : (pwfRecv->wFormatTag == 112) ? "LHCELP" : (pwfRecv->wFormatTag == 113) ? "LHSB08" : (pwfRecv->wFormatTag == 114) ? "LHSB12" : (pwfRecv->wFormatTag == 115) ? "LHSB16" : (pwfRecv->wFormatTag == 6) ? "MSALAW" : (pwfRecv->wFormatTag == 7) ? "MSULAW" : (pwfRecv->wFormatTag == 130) ? "MSRT24" : "??????")); RETAILMSG(("NAC: Audio Recv Sampling Rate (Hz): %ld", pwfRecv->nSamplesPerSec)); RETAILMSG(("NAC: Audio Recv Bitrate (w/o network overhead - bps): %ld", pwfRecv->nAvgBytesPerSec*8)); // Initialize the recv-stream media control object mcConfig.pDevFmt = &m_fDevRecv; mcConfig.hStrm = (DPHANDLE) m_RecvStream; mcConfig.uDevId = m_RenderingDevice; mcConfig.cbSamplesPerPkt = audChannelParams.ns_params.wFrameSize *audChannelParams.ns_params.wFramesPerPkt; UPDATE_REPORT_ENTRY(g_prptCallParameters, mcConfig.cbSamplesPerPkt, REP_RECV_AUDIO_PACKET); RETAILMSG(("NAC: Audio Recv Packetization (ms/packet): %ld", pwfRecv->nSamplesPerSec ? mcConfig.cbSamplesPerPkt * 1000UL / pwfRecv->nSamplesPerSec : 0)); INIT_COUNTER_MAX(g_pctrAudioReceiveBytes, (pwfRecv->nAvgBytesPerSec + pwfRecv->nSamplesPerSec * (sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE) / mcConfig.cbSamplesPerPkt) << 3); hr = m_OutMedia->Configure(&mcConfig); // Check if we can open the wave device. This is just to give advance notice of // sound card being busy. // Stop any high level ("PlaySound()") usage of wave device. // PlaySound(NULL,NULL, 0); // if (hr == DPR_SUCCESS && !(dwFlags & DP_FLAG_HALF_DUPLEX)) { // hr = m_OutMedia->Open (); // } // if (hr != DPR_SUCCESS) // { // DEBUGMSG (ZONE_DP, ("%s: OMedia->Config failed, hr=0x%lX\r\n", _fx_, hr)); // goto OMediaInitError; // } mmr = m_pAudioFilter->Open(pwfRecv, &m_fDevRecv); if (mmr != 0) { DEBUGMSG (ZONE_DP, ("%s: AcmFilter->Open failed, mmr=%d\r\n", _fx_, mmr)); hr = DPR_CANT_OPEN_CODEC; goto RecvFilterInitError; } // Initialize the recv stream ZeroMemory (&apInit, sizeof (apInit)); apInit.dwFlags = DP_FLAG_RECV | DP_FLAG_ACM | DP_FLAG_MMSYSTEM; apInit.pStrmConvSrcFmt = pwfRecv; apInit.pStrmConvDstFmt = &m_fDevRecv; m_OutMedia->FillMediaPacketInit (&apInit); apInit.cbSizeRawData = apInit.cbSizeDevData; m_pAudioFilter->SuggestSrcSize(apInit.cbSizeDevData, &m_dwSrcSize); apInit.cbSizeNetData = m_dwSrcSize; apInit.cbOffsetNetData = sizeof (RTP_HDR); m_OutMedia->GetProp (MC_PROP_SPP, &dwPropVal); // set our total receive buffering capacity to somewhere between // 2 and 4 seconds. // Also make sure that the buffering capacity is at least one // second more than maxAudioDelay maxRingSamples = pwfRecv->nSamplesPerSec + pwfRecv->nSamplesPerSec*g_MaxAudioDelayMs/1000; if (maxRingSamples < 4*pwfRecv->nSamplesPerSec) maxRingSamples = 4*pwfRecv->nSamplesPerSec; while (ringSize* dwPropVal > maxRingSamples && ringSize > 8) ringSize = ringSize/2; dwFlags = DP_FLAG_MMSYSTEM; // if sender is not doing silence detection, we do it // on the receive side if (!audChannelParams.ns_params.UseSilenceDet) dwFlags |= DP_FLAG_AUTO_SILENCE_DETECT; fRet = m_RecvStream->Initialize (dwFlags, ringSize, NULL, &apInit, (DWORD)dwPropVal, pwfRecv->nSamplesPerSec, m_pAudioFilter); if (! fRet) { DEBUGMSG (ZONE_DP, ("%s: RxStream->Init failed, fRet=0%u\r\n", _fx_, fRet)); hr = DPR_CANT_INIT_RX_STREAM; goto RxStreamInitError; } // WS2Qos will be called in Start to communicate stream reservations to the // remote endpoint using a RESV message // // We use a peak-rate allocation approach based on our target bitrates // Note that for the token bucket size and the maximum SDU size, we now // account for IP header overhead, and use the max frame fragment size // instead of the maximum compressed image size returned by the codec // // Some of the parameters are left unspecified because they are set // in the sender Tspec. InitAudioFlowspec(&m_flowspec, pwfRecv, m_dwSrcSize); // prepare headers for RxStream m_RecvStream->GetRing (&ppAudPckt, &cAudPckt); m_OutMedia->RegisterData (ppAudPckt, cAudPckt); // m_OutMedia->PrepareHeaders (); m_pAudioFilter->PrepareAudioPackets((AudioPacket**)ppAudPckt, cAudPckt, AP_DECODE); // Open the record to wav file AudioFile::OpenDestFile(&m_mmioDest, &m_fDevRecv); m_DPFlags |= DPFLAG_CONFIGURED_RECV; #ifdef TEST LOG((LOGMSG_TIME_RECV_AUDIO_CONFIGURE,GetTickCount() - dwTicks)); #endif return DPR_SUCCESS; RxStreamInitError: RecvFilterInitError: m_pAudioFilter->Close(); m_OutMedia->Close(); //OMediaInitError: if (m_pIRTPRecv) { m_pIRTPRecv->Release(); m_pIRTPRecv = NULL; } ERRORMESSAGE(("%s: failed, hr=0%u\r\n", _fx_, hr)); return hr; } void RecvAudioStream::UnConfigure() { AudioPacket **ppAudPckt=NULL; ULONG uPackets; #ifdef TEST DWORD dwTicks; dwTicks = GetTickCount(); #endif if ((m_DPFlags & DPFLAG_CONFIGURED_RECV)) { Stop(); // Close the RTP state if its open //m_Net->Close(); We should be able to do this in Disconnect() m_Net = NULL; m_OutMedia->Reset(); m_OutMedia->UnprepareHeaders(); m_OutMedia->Close(); // Close the record to wav file AudioFile::CloseDestFile(&m_mmioDest); // Close the filters m_RecvStream->GetRing ((MediaPacket***)&ppAudPckt, &uPackets); m_pAudioFilter->UnPrepareAudioPackets(ppAudPckt, uPackets, AP_DECODE); m_pAudioFilter->Close(); // Close the receive streams m_RecvStream->Destroy(); m_DPFlags &= ~(DPFLAG_CONFIGURED_RECV); } #ifdef TEST LOG((LOGMSG_TIME_RECV_AUDIO_UNCONFIGURE,GetTickCount() - dwTicks)); #endif } DWORD CALLBACK RecvAudioStream::StartPlaybackThread(LPVOID pVoid) { RecvAudioStream *pThisStream = (RecvAudioStream *)pVoid; return pThisStream->PlaybackThread(); } HRESULT RecvAudioStream::Start() { FX_ENTRY ("RecvAudioStream::Start"); if (m_DPFlags & DPFLAG_STARTED_RECV) return DPR_SUCCESS; // TODO: remove this check once audio UI calls the IComChan PAUSE_RECV prop if (!(m_DPFlags & DPFLAG_ENABLE_RECV)) return DPR_SUCCESS; if ((!(m_DPFlags & DPFLAG_CONFIGURED_RECV)) || (NULL==m_pIRTPRecv)) return DPR_NOT_CONFIGURED; ASSERT(!m_hRenderingThread ); m_ThreadFlags &= ~(DPTFLAG_STOP_PLAY|DPTFLAG_STOP_RECV); SetFlowSpec(); // Start playback thread if (!(m_ThreadFlags & DPTFLAG_STOP_PLAY)) m_hRenderingThread = CreateThread(NULL,0,RecvAudioStream::StartPlaybackThread,this,0,&m_RenderingThId); // Start receive thread m_pDP->StartReceiving(this); m_DPFlags |= DPFLAG_STARTED_RECV; DEBUGMSG (ZONE_DP, ("%s: Play ThId=%x\r\n",_fx_, m_RenderingThId)); return DPR_SUCCESS; } // LOOK: Identical to RecvVideoStream version. HRESULT RecvAudioStream::Stop() { DWORD dwWait; FX_ENTRY ("RecvAudioStream::Stop"); if(!(m_DPFlags & DPFLAG_STARTED_RECV)) { return DPR_SUCCESS; } m_ThreadFlags = m_ThreadFlags | DPTFLAG_STOP_RECV | DPTFLAG_STOP_PLAY ; m_pDP->StopReceiving(this); DEBUGMSG (ZONE_VERBOSE, ("%s: hRenderingThread=%x\r\n",_fx_, m_hRenderingThread)); /* * we want to wait for all the threads to exit, but we need to handle windows * messages (mostly from winsock) while waiting. */ if(m_hRenderingThread) { dwWait = WaitForSingleObject(m_hRenderingThread, INFINITE); DEBUGMSG (ZONE_VERBOSE, ("%s: dwWait =%d\r\n", _fx_, dwWait)); ASSERT(dwWait != WAIT_FAILED); CloseHandle(m_hRenderingThread); m_hRenderingThread = NULL; } //This is per channel, but the variable is "DPFlags" m_DPFlags &= ~DPFLAG_STARTED_RECV; return DPR_SUCCESS; } // low order word is the signal strength // high order work contains bits to indicate status // (0x01 - receiving (actually playing)) // (0x02 - audio device is jammed) STDMETHODIMP RecvAudioStream::GetSignalLevel(UINT *pSignalStrength) { DWORD dwLevel; DWORD dwJammed; DWORD_PTR dwPropVal; if ( (!(m_DPFlags & DPFLAG_STARTED_RECV)) || (m_ThreadFlags & DPTFLAG_PAUSE_RECV)) { dwLevel = 0; } else { m_RecvStream->GetSignalStrength(&dwLevel); dwLevel = (dwLevel >> 8) & 0x00ff; dwLevel = LogScale[dwLevel]; m_OutMedia->GetProp(MC_PROP_AUDIO_JAMMED, &dwPropVal); dwJammed = (DWORD)dwPropVal; if (dwJammed) { dwLevel = (2 << 16); } else if (m_fReceiving) { dwLevel |= (1 << 16); } } *pSignalStrength = dwLevel; return S_OK; }; // IProperty::GetProperty / SetProperty // (DataPump::MediaChannel::GetProperty) // Properties of the MediaChannel. Supports properties for both audio // and video channels. STDMETHODIMP RecvAudioStream::GetProperty( DWORD prop, PVOID pBuf, LPUINT pcbBuf ) { HRESULT hr = DPR_SUCCESS; RTP_STATS RTPStats; DWORD_PTR dwPropVal; UINT len = sizeof(DWORD); // most props are DWORDs if (!pBuf || *pcbBuf < len) { *pcbBuf = len; return DPR_INVALID_PARAMETER; } switch (prop) { case PROP_RECV_AUDIO_STRENGTH: return GetSignalLevel((UINT *)pBuf); case PROP_AUDIO_JAMMED: hr = m_OutMedia->GetProp(MC_PROP_AUDIO_JAMMED, &dwPropVal); *(DWORD *)pBuf = (DWORD)dwPropVal; break; #ifdef OLDSTUFF case PROP_NET_RECV_STATS: if (m_Net && *pcbBuf >= sizeof(RTP_STATS)) { m_Net->GetRecvStats((RTP_STATS *)pBuf); *pcbBuf = sizeof(RTP_STATS); } else hr = DPR_INVALID_PROP_VAL; break; #endif case PROP_DURATION: hr = m_OutMedia->GetProp(MC_PROP_DURATION, &dwPropVal); *(DWORD *)pBuf = (DWORD)dwPropVal; break; case PROP_VOLUME: hr = m_OutMedia->GetProp(MC_PROP_VOLUME, &dwPropVal); *(DWORD *)pBuf = (DWORD)dwPropVal; break; case PROP_DUPLEX_TYPE: hr = m_OutMedia->GetProp(MC_PROP_DUPLEX_TYPE, &dwPropVal); if(HR_SUCCEEDED(hr)) { if(dwPropVal & DP_FLAG_FULL_DUPLEX) *(DWORD *)pBuf = DUPLEX_TYPE_FULL; else *(DWORD *)pBuf = DUPLEX_TYPE_HALF; } break; case PROP_AUDIO_SPP: hr = m_OutMedia->GetProp(MC_PROP_SPP, &dwPropVal); *(DWORD *)pBuf = (DWORD)dwPropVal; break; case PROP_AUDIO_SPS: hr = m_OutMedia->GetProp(MC_PROP_SPS, &dwPropVal); *(DWORD *)pBuf = (DWORD)dwPropVal; break; case PROP_WAVE_DEVICE_TYPE: *(DWORD *)pBuf = m_DPFlags & DP_MASK_WAVE_DEVICE; break; case PROP_PLAY_ON: *(DWORD *)pBuf = (m_ThreadFlags & DPFLAG_ENABLE_RECV)!=0; break; case PROP_PLAYBACK_DEVICE: *(DWORD *)pBuf = m_RenderingDevice; break; case PROP_VIDEO_AUDIO_SYNC: *(DWORD *)pBuf = ((m_DPFlags & DPFLAG_AV_SYNC) != 0); break; default: hr = DPR_INVALID_PROP_ID; break; } return hr; } STDMETHODIMP RecvAudioStream::SetProperty( DWORD prop, PVOID pBuf, UINT cbBuf ) { DWORD_PTR dwPropVal; HRESULT hr = S_OK; if (cbBuf < sizeof (DWORD)) return DPR_INVALID_PARAMETER; switch (prop) { case PROP_VOLUME: dwPropVal = *(DWORD *)pBuf; hr = m_OutMedia->SetProp(MC_PROP_VOLUME, dwPropVal); break; case PROP_DUPLEX_TYPE: ASSERT(0); break; case DP_PROP_DUPLEX_TYPE: // internal version, called by DataPump::SetDuplexMode() after ensuring streams are stopped dwPropVal = *(DWORD *)pBuf; if (dwPropVal) { dwPropVal = DP_FLAG_FULL_DUPLEX; } else { dwPropVal = DP_FLAG_HALF_DUPLEX; } m_OutMedia->SetProp(MC_PROP_DUPLEX_TYPE, dwPropVal); break; case PROP_PLAY_ON: { if (*(DWORD *)pBuf) // unmute { m_ThreadFlags &= ~DPTFLAG_PAUSE_RECV; } else // mute { m_ThreadFlags |= DPTFLAG_PAUSE_RECV; } // DWORD flag = DPFLAG_ENABLE_RECV; // if (*(DWORD *)pBuf) { // m_DPFlags |= flag; // set the flag // hr = Start(); // } // else // { // m_DPFlags &= ~flag; // clear the flag // hr = Stop(); // } RETAILMSG(("NAC: %s", *(DWORD *)pBuf ? "Enabling":"Disabling")); break; } case PROP_PLAYBACK_DEVICE: m_RenderingDevice = *(DWORD *)pBuf; RETAILMSG(("NAC: Setting default playback device to %d", m_RenderingDevice)); break; case PROP_VIDEO_AUDIO_SYNC: if (*(DWORD *)pBuf) m_DPFlags |= DPFLAG_AV_SYNC; else m_DPFlags &= ~DPFLAG_AV_SYNC; break; default: return DPR_INVALID_PROP_ID; break; } return hr; } HRESULT RecvAudioStream::GetCurrentPlayNTPTime(NTP_TS *pNtpTime) { DWORD rtpTime; #ifdef OLDSTUFF if ((m_DPFlags & DPFLAG_STARTED_RECV) && m_fReceiving) { if (m_Net->RTPtoNTP(m_PlaybackTimestamp,pNtpTime)) return S_OK; } #endif return 0xff; // return proper error } BOOL RecvAudioStream::IsEmpty() { return m_RecvStream->IsEmpty(); } /* Called by the recv thread to setup the stream for receiving. Post the initial recv buffer(s). Subsequently, the buffers are posted in the RTPRecvCallback() */ HRESULT RecvAudioStream::StartRecv(HWND hWnd) { HRESULT hr = S_OK; DWORD dwPropVal = 0; if ((!(m_ThreadFlags & DPTFLAG_STOP_RECV) ) && (m_DPFlags & DPFLAG_CONFIGURED_RECV)){ // m_RecvFilter->GetProp (FM_PROP_SRC_SIZE, &dwPropVal); hr =m_pIRTPRecv->SetRecvNotification(&RTPRecvCallback, (DWORD_PTR)this, 2); } return hr; } /* Called by the recv thread to suspend receiving on this RTP session If there are outstanding receive buffers they have to be recovered */ HRESULT RecvAudioStream::StopRecv() { // dont recv on this stream m_pIRTPRecv->CancelRecvNotification(); return S_OK; } HRESULT RecvAudioStream::RTPCallback(WSABUF *pWsaBuf, DWORD timestamp, UINT seq, UINT fMark) { HRESULT hr; DWORD_PTR dwPropVal; // if we are paused, reject the packet if (m_ThreadFlags & DPTFLAG_PAUSE_RECV) { return E_FAIL; } // The last two parameters are only used by the recv video stream hr = m_RecvStream->PutNextNetIn(pWsaBuf, timestamp, seq, fMark, NULL, NULL); m_pIRTPRecv->FreePacket(pWsaBuf); if (SUCCEEDED(hr)) { m_OutMedia->GetProp (MC_PROP_EVENT_HANDLE, &dwPropVal); if (dwPropVal) { SetEvent( (HANDLE) dwPropVal); } else { DEBUGMSG(ZONE_DP,("PutNextNetIn (ts=%d,seq=%d,fMark=%d) failed with 0x%lX\r\n",timestamp,seq,fMark,hr)); } } return S_OK; } // global RTP callback function for all receive streams BOOL RTPRecvCallback( DWORD_PTR dwCallback, WSABUF *pNetRecvBuf ) { HRESULT hr; DWORD timestamp; UINT seq; BOOL fMark; RecvMediaStream *pRecvMC = (RecvMediaStream *)dwCallback; RTP_HDR *pRTPHdr; pRTPHdr = (RTP_HDR *)pNetRecvBuf->buf; timestamp = pRTPHdr->ts; seq = pRTPHdr->seq; fMark = pRTPHdr->m; // packet looks okay LOG((LOGMSG_NET_RECVD,timestamp,seq,GetTickCount())); hr = pRecvMC->RTPCallback(pNetRecvBuf,timestamp,seq,fMark); if (SUCCEEDED(hr)) { return TRUE; } return FALSE; } #define MAX_SILENCE_LEVEL 75*256 #define MIN_SILENCE_LEVEL 10*256 AudioSilenceDetector::AudioSilenceDetector() { // initialize silence detector stats // start with a high value because the estimator falls fast but rises slowly m_iSilenceAvg = MAX_SILENCE_LEVEL - MIN_SILENCE_LEVEL; m_iTalkAvg = 0; m_iSilenceLevel = MAX_SILENCE_LEVEL; m_uManualSilenceLevel = 1000; // use auto mode. } // update adaptive silence threshold variables in SendAudioStats // using m_dwMaxStrength (the max. peak to peak value in a buffer) // return TRUE if below threshold BOOL AudioSilenceDetector::SilenceDetect(WORD wStrength) { int fSilence; INT strength; m_dwMaxStrength = wStrength; strength = LogScale[m_dwMaxStrength >> 8] << 8; // UI sets the silence threshold high ( == 1000/1000) to indicate // automatic silence detection if (m_uManualSilenceLevel >= 1000) { LOG((LOGMSG_AUTO_SILENCE,strength >> 8,m_iSilenceLevel >> 8,m_iSilenceAvg>>8)); if (strength > m_iSilenceLevel) { // talking // increase threshold slowly // BUGBUG: should depend on time interval m_iSilenceLevel += 50; //increased from 25- GJ m_iTalkAvg += (strength -m_iTalkAvg)/16; fSilence = FALSE; } else { // silence // update the average silence level m_iSilenceAvg += (strength - m_iSilenceAvg)/16; // set the threshold to the avg silence + a constant m_iSilenceLevel = m_iSilenceAvg + MIN_SILENCE_LEVEL; fSilence = TRUE; } if (m_iSilenceLevel > MAX_SILENCE_LEVEL) m_iSilenceLevel = MAX_SILENCE_LEVEL; } else { // use the user-specified silence threshold // oddly, the manual silence level is in a different range [0,1000] DWORD dwSilenceLevel = m_uManualSilenceLevel * 65536/1000; fSilence = (m_dwMaxStrength < dwSilenceLevel); LOG((LOGMSG_AUTO_SILENCE,m_dwMaxStrength, dwSilenceLevel ,0)); } return fSilence; } // this method called from the UI thread only HRESULT RecvAudioStream::DTMFBeep() { int nBeeps; MediaPacket **ppAudPckt=NULL, *pPacket=NULL; void *pBuffer; ULONG uCount; UINT uBufferSize=0; if ( (!(m_DPFlags & DPFLAG_STARTED_RECV)) || (m_ThreadFlags & DPTFLAG_PAUSE_RECV) ) { return E_FAIL; } // how many packets do we inject into the stream ? m_RecvStream->GetRing(&ppAudPckt, &uCount); pPacket = ppAudPckt[0]; pPacket->GetDevData(&pBuffer, &uBufferSize); if (uBufferSize == 0) { return E_FAIL; } nBeeps = DTMF_FEEDBACK_BEEP_MS / ((uBufferSize * 1000) / m_fDevRecv.nAvgBytesPerSec); if (nBeeps == 0) { nBeeps = 1; } m_RecvStream->InjectBeeps(nBeeps); return S_OK; }