windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/nac/recvaudio.cpp

960 lines
23 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#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;
}