960 lines
23 KiB
C++
960 lines
23 KiB
C++
#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;
|
||
}
|
||
|
||
|
||
|