|
|
#include "precomp.h"
#include <nmdsprv.h>
#include "mixer.h"
#include "dscstream.h"
#include "agc.h"
// static member initialization
BOOL DSC_Manager::s_bInitialized = FALSE; DSC_CAPTURE_INFO DSC_Manager::s_aDSC[MAX_NUMBER_DSCAPTURE_DEVICES]; int DSC_Manager::s_nCaptureDevices = 0; HINSTANCE DSC_Manager::s_hDSCLib = NULL; DS_CAP_CREATE DSC_Manager::s_pDSCapCreate = NULL; DS_CAP_ENUM DSC_Manager::s_pDSCapEnum = NULL;
// static
HRESULT DSC_Manager::Initialize() {
if (s_bInitialized) { return S_OK; }
// failsafe way to to turn DSC off, without turning
// DirectSound support off. Otherwise, the UI setting
// to disable DS will also disable DSC.
{ BOOL bDisable; RegEntry re(DISABLE_DSC_REGKEY, HKEY_LOCAL_MACHINE, FALSE,0);
bDisable = re.GetNumber(DISABLE_DSC_REGVALUE, FALSE); if (bDisable) { return E_FAIL; } }
// initialize the array of structure descriptions
s_hDSCLib = LoadLibrary(DSOUND_DLL);
if (s_hDSCLib == NULL) return E_FAIL;
s_pDSCapCreate = (DS_CAP_CREATE)GetProcAddress(s_hDSCLib, "DirectSoundCaptureCreate"); s_pDSCapEnum = (DS_CAP_ENUM)GetProcAddress(s_hDSCLib, "DirectSoundCaptureEnumerateA");
if ((s_pDSCapCreate) && (s_pDSCapEnum)) { // enumerate!
s_pDSCapEnum(DSC_Manager::DSEnumCallback, 0);
if (s_nCaptureDevices != 0) { s_bInitialized = TRUE; return S_OK; // success
} }
FreeLibrary(s_hDSCLib); s_hDSCLib = NULL; return E_FAIL;
}
// static
BOOL CALLBACK DSC_Manager::DSEnumCallback(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) { if (lpGuid == NULL) { s_aDSC[s_nCaptureDevices].guid = GUID_NULL; } else { s_aDSC[s_nCaptureDevices].guid = *lpGuid; }
lstrcpyn(s_aDSC[s_nCaptureDevices].szDescription, lpcstrDescription, MAX_DSC_DESCRIPTION_STRING);
s_aDSC[s_nCaptureDevices].uWaveId = WAVE_MAPPER; s_nCaptureDevices++; return TRUE; }
// static
HRESULT DSC_Manager::CreateInstance(GUID *pGuid, IDirectSoundCapture **pDSC) { HRESULT hr;
if FAILED(Initialize()) { return E_FAIL; }
if (*pGuid == GUID_NULL) pGuid = NULL;
hr = s_pDSCapCreate(pGuid, pDSC, NULL);
return hr; }
// static
HRESULT DSC_Manager::MapWaveIdToGuid(UINT uWaveID, GUID *pGuid) {
HRESULT hr; WAVEINCAPS waveInCaps; UINT uNumWaveDevs; GUID guid = GUID_NULL; int nIndex; MMRESULT mmr; HWAVEIN hWaveIn; WAVEFORMATEX waveFormat = {WAVE_FORMAT_PCM, 1, 8000, 16000, 2, 16, 0}; IDirectSoundCapture *pDSC=NULL;
*pGuid = GUID_NULL; if (FAILED( Initialize() )) { return E_FAIL; }
// only one wave device, take the easy way out
uNumWaveDevs = waveInGetNumDevs();
if ((uNumWaveDevs <= 1) || (uWaveID == WAVE_MAPPER)) { return S_OK; }
// more than one wavein device
mmr = waveInGetDevCaps(uWaveID, &waveInCaps, sizeof(WAVEINCAPS)); if (mmr == MMSYSERR_NOERROR) { hr = DsprvGetWaveDeviceMapping(waveInCaps.szPname, TRUE, &guid); if (SUCCEEDED(hr)) { *pGuid = guid; return S_OK; } }
// scan through the DSC list to see if we've mapped this device
// previously
for (nIndex = 0; nIndex < s_nCaptureDevices; nIndex++) { if (s_aDSC[nIndex].uWaveId == uWaveID) { *pGuid = s_aDSC[nIndex].guid; return S_OK; } }
// hack approach to mapping the device to a guid
mmr = waveInOpen(&hWaveIn, uWaveID, &waveFormat, 0,0,0); if (mmr != MMSYSERR_NOERROR) { return S_FALSE; }
// find all the DSC devices that fail to open
for (nIndex = 0; nIndex < s_nCaptureDevices; nIndex++) { s_aDSC[nIndex].bAllocated = FALSE; hr = CreateInstance(&(s_aDSC[nIndex].guid), &pDSC); if (FAILED(hr)) { s_aDSC[nIndex].bAllocated = TRUE; } else { pDSC->Release(); pDSC=NULL; } }
waveInClose(hWaveIn);
// scan through the list of allocated devices and
// see which one opens
for (nIndex = 0; nIndex < s_nCaptureDevices; nIndex++) { if (s_aDSC[nIndex].bAllocated) { hr = CreateInstance(&(s_aDSC[nIndex].guid), &pDSC); if (SUCCEEDED(hr)) { // we have a winner
pDSC->Release(); pDSC = NULL; *pGuid = s_aDSC[nIndex].guid; s_aDSC[nIndex].uWaveId = uWaveID; return S_OK; } } } // if we got to this point, it means we failed to map a device
// just use GUID_NULL and return an error
return S_FALSE; }
SendDSCStream::SendDSCStream() : SendMediaStream(), m_pAudioFilter(NULL), m_lRefCount(0), m_pDSC(NULL), m_pDSCBuffer(NULL), m_hEvent(NULL), m_dwSamplesPerFrame(0), m_dwNumFrames(0), m_dwFrameSize(0), m_dwDSCBufferSize(0), m_dwSilenceTime(0), m_dwFrameTimeMS(0), m_bFullDuplex(TRUE), m_bJammed(FALSE), m_bCanSignalOpen(TRUE), m_bCanSignalFail(TRUE), m_nFailCount(0), m_agc(NULL), m_bAutoMix(FALSE), m_pDTMF(NULL) { return; };
HRESULT SendDSCStream::Initialize(DataPump *pDP) { HRESULT hr;
m_pDP = pDP;
hr = DSC_Manager::Initialize(); if (FAILED(hr)) { return hr; }
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hEvent == NULL) { return DPR_CANT_CREATE_EVENT; }
DBG_SAVE_FILE_LINE m_pAudioFilter = new AcmFilter(); if (!m_pAudioFilter) { return DPR_OUT_OF_MEMORY; }
DBG_SAVE_FILE_LINE m_pDTMF = new DTMFQueue; if (!m_pDTMF) { return DPR_OUT_OF_MEMORY; }
m_DPFlags = DP_FLAG_ACM|DP_FLAG_MMSYSTEM|DP_FLAG_DIRECTSOUND|DP_FLAG_SEND;
m_SendTimestamp = m_SavedTickCount = timeGetTime();
m_dwDstSize = 0; m_fSending = FALSE;
m_hCapturingThread = NULL; m_CaptureThId = 0; m_ThreadFlags = 0;
m_pRTPSend = NULL; m_RTPPayload = 0;
m_CaptureDevice = -1; m_pRTPSend = NULL;
ZeroMemory(m_aPackets, sizeof(m_aPackets)); ZeroMemory(&m_mmioSrc, sizeof(m_mmioSrc));
m_DPFlags = DP_FLAG_ACM | DP_FLAG_MMSYSTEM | DP_FLAG_AUTO_SILENCE_DETECT; m_DPFlags = (m_DPFlags & DP_MASK_PLATFORM) | DPFLAG_ENABLE_SEND; m_DPFlags |= DPFLAG_INITIALIZED;
return S_OK;
}
SendDSCStream::~SendDSCStream() { if (m_DPFlags & DPFLAG_INITIALIZED) { if (m_DPFlags & DPFLAG_CONFIGURED_SEND) { UnConfigure(); }
if (m_pRTPSend) { m_pRTPSend->Release(); m_pRTPSend = NULL; }
if (m_pDTMF) { delete m_pDTMF; m_pDTMF = NULL; }
if (m_pAudioFilter) { delete m_pAudioFilter; }
if (m_hEvent) { CloseHandle(m_hEvent); }
m_pDP->RemoveMediaChannel(MCF_SEND|MCF_AUDIO, (IMediaChannel*)(SendMediaStream*)this);
m_DPFlags &= ~DPFLAG_INITIALIZED; } }
HRESULT STDMETHODCALLTYPE SendDSCStream::Configure( BYTE *pFormat, UINT cbFormat, BYTE *pChannelParams, UINT cbParams, IUnknown *pUnknown) { AUDIO_CHANNEL_PARAMETERS audChannelParams; WAVEFORMATEX *pwfSend; MMRESULT mmr; MEDIAPACKETINIT mpi; DWORD dwSourceSize; int nIndex; HRESULT hr;
FX_ENTRY ("SendDSCStream::Configure");
// basic parameter checking
if (! (m_DPFlags & DPFLAG_INITIALIZED)) { return DPR_OUT_OF_MEMORY; }
// Not a good idea to change anything while in mid-stream
if (m_DPFlags & DPFLAG_STARTED_SEND) { return DPR_IO_PENDING; // anything better to return
}
if (m_DPFlags & DPFLAG_CONFIGURED_SEND) { DEBUGMSG(ZONE_DP, ("Stream Re-Configuration - calling UnConfigure")); UnConfigure(); }
if ((NULL == pFormat) || (NULL == pChannelParams) || (cbParams < sizeof(AUDIO_CHANNEL_PARAMETERS)) || (cbFormat < sizeof(WAVEFORMATEX))) { return DPR_INVALID_PARAMETER; }
audChannelParams = *(AUDIO_CHANNEL_PARAMETERS *)pChannelParams; pwfSend = (WAVEFORMATEX *)pFormat; m_wfCompressed = *pwfSend; m_wfCompressed.cbSize = 0;
// initialize the ACM filter
mmr = AcmFilter::SuggestDecodeFormat(pwfSend, &m_wfPCM); if (mmr != MMSYSERR_NOERROR) { return DPR_INVALID_PARAMETER; }
mmr = m_pAudioFilter->Open(&m_wfPCM, pwfSend); if (mmr != 0) { DEBUGMSG (ZONE_DP, ("%s: AcmFilter->Open failed, mmr=%d\r\n", _fx_, mmr)); return DPR_CANT_OPEN_CODEC; }
m_dwSamplesPerFrame = audChannelParams.ns_params.wFrameSize * audChannelParams.ns_params.wFramesPerPkt; m_dwFrameTimeMS = (m_dwSamplesPerFrame * 1000) / m_wfPCM.nSamplesPerSec;
ASSERT(m_dwFrameTimeMS > 0); if (m_dwFrameTimeMS <= 0) { m_pAudioFilter->Close(); return DPR_INVALID_PARAMETER; }
m_dwNumFrames = 1000 / m_dwFrameTimeMS; if (m_dwNumFrames < MIN_NUM_DSC_SEGMENTS) { m_dwNumFrames = MIN_NUM_DSC_SEGMENTS; }
m_dwFrameSize = m_dwSamplesPerFrame * m_wfPCM.nBlockAlign; m_pAudioFilter->SuggestDstSize(m_dwFrameSize, &m_dwDstSize);
m_dwDSCBufferSize = m_dwFrameSize * m_dwNumFrames;
// create the packets
ZeroMemory(&mpi, sizeof(mpi));
mpi.dwFlags = DP_FLAG_SEND | DP_FLAG_ACM | DP_FLAG_MMSYSTEM; mpi.cbOffsetNetData = sizeof(RTP_HDR); mpi.cbSizeNetData = m_dwDstSize; mpi.cbSizeDevData = m_dwFrameSize; mpi.cbSizeRawData = m_dwFrameSize; mpi.pDevFmt = &m_wfPCM; mpi.pStrmConvSrcFmt = &m_wfPCM; mpi.payload = audChannelParams.RTP_Payload; mpi.pStrmConvDstFmt = &m_wfCompressed;
hr = CreateAudioPackets(&mpi); if (FAILED(hr)) { m_pAudioFilter->Close(); return hr; }
AudioFile::OpenSourceFile(&m_mmioSrc, &m_wfPCM);
m_pDTMF->Initialize(&m_wfPCM); m_pDTMF->ClearQueue();
// Initialize RSVP structures
InitAudioFlowspec(&m_flowspec, pwfSend, m_dwDstSize);
// Initialize QOS structures
if (m_pDP->m_pIQoS) { // Initialize our requests. One for CPU usage, one for bandwidth usage.
m_aRRq.cResourceRequests = 2; m_aRRq.aResourceRequest[0].resourceID = RESOURCE_OUTGOING_BANDWIDTH; if (m_dwFrameTimeMS) { m_aRRq.aResourceRequest[0].nUnitsMin = (DWORD)(m_dwDstSize + sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE) * 8000 / m_dwFrameTimeMS; } else { m_aRRq.aResourceRequest[0].nUnitsMin = 0; } m_aRRq.aResourceRequest[1].resourceID = RESOURCE_CPU_CYCLES; m_aRRq.aResourceRequest[1].nUnitsMin = 800;
// BUGBUG. This is, in theory the correct calculation, but until we do more investigation, go with a known value
// m_aRRq.aResourceRequest[1].nUnitsMin = (audDetails.wCPUUtilizationEncode+audDetails.wCPUUtilizationDecode)*10;
// Initialize QoS structure
ZeroMemory(&m_Stats, sizeof(m_Stats));
// Initialize oldest QoS callback timestamp
// Register with the QoS module. Even if this call fails, that's Ok, we'll do without the QoS support
// The Callback is defined in SendAudioStream
m_pDP->m_pIQoS->RequestResources((GUID *)&MEDIA_TYPE_H323AUDIO, (LPRESOURCEREQUESTLIST)&m_aRRq, SendAudioStream::QosNotifyAudioCB, (DWORD_PTR)this); }
// Initialize Statview constats
UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfSend->wFormatTag, REP_SEND_AUDIO_FORMAT); UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfSend->nSamplesPerSec, REP_SEND_AUDIO_SAMPLING); UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfSend->nAvgBytesPerSec * 8, REP_SEND_AUDIO_BITRATE); RETAILMSG(("NAC: Audio Send Format: %s", (pwfSend->wFormatTag == 66) ? "G723.1" : (pwfSend->wFormatTag == 112) ? "LHCELP" : (pwfSend->wFormatTag == 113) ? "LHSB08" : (pwfSend->wFormatTag == 114) ? "LHSB12" : (pwfSend->wFormatTag == 115) ? "LHSB16" : (pwfSend->wFormatTag == 6) ? "MSALAW" : (pwfSend->wFormatTag == 7) ? "MSULAW" : (pwfSend->wFormatTag == 130) ? "MSRT24" : "??????")); RETAILMSG(("NAC: Audio Send Sampling Rate (Hz): %ld", pwfSend->nSamplesPerSec)); RETAILMSG(("NAC: Audio Send Bitrate (w/o network overhead - bps): %ld", pwfSend->nAvgBytesPerSec*8));
UPDATE_REPORT_ENTRY(g_prptCallParameters, m_dwSamplesPerFrame, REP_SEND_AUDIO_PACKET); RETAILMSG(("NAC: Audio Send Packetization (ms/packet): %ld", pwfSend->nSamplesPerSec ? m_dwSamplesPerFrame * 1000UL / pwfSend->nSamplesPerSec : 0)); INIT_COUNTER_MAX(g_pctrAudioSendBytes, (pwfSend->nAvgBytesPerSec + pwfSend->nSamplesPerSec * (sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE) / m_dwSamplesPerFrame) << 3);
m_DPFlags |= DPFLAG_CONFIGURED_SEND; return S_OK; }
void SendDSCStream::UnConfigure() { if (m_DPFlags & DPFLAG_CONFIGURED_SEND) { Stop();
m_pAudioFilter->Close(); ReleaseAudioPackets();
AudioFile::CloseSourceFile(&m_mmioSrc);
m_ThreadFlags = 0;
if (m_pDP->m_pIQoS) { m_pDP->m_pIQoS->ReleaseResources((GUID *)&MEDIA_TYPE_H323AUDIO, (LPRESOURCEREQUESTLIST)&m_aRRq); }
m_DPFlags &= ~DPFLAG_CONFIGURED_SEND;
} }
DWORD CALLBACK SendDSCStream::StartRecordingThread (LPVOID pVoid) { SendDSCStream *pThisStream = (SendDSCStream*)pVoid; return pThisStream->RecordingThread(); }
HRESULT STDMETHODCALLTYPE SendDSCStream::Start() { FX_ENTRY ("SendDSCStream::Start")
if (m_DPFlags & DPFLAG_STARTED_SEND) return DPR_SUCCESS;
if (!(m_DPFlags & DPFLAG_ENABLE_SEND)) return DPR_SUCCESS;
if ((!(m_DPFlags & DPFLAG_CONFIGURED_SEND)) || (m_pRTPSend==NULL)) return DPR_NOT_CONFIGURED;
ASSERT(!m_hCapturingThread); m_ThreadFlags &= ~(DPTFLAG_STOP_RECORD|DPTFLAG_STOP_SEND);
SetFlowSpec();
// Start recording thread
if (!(m_ThreadFlags & DPTFLAG_STOP_RECORD)) m_hCapturingThread = CreateThread(NULL,0, SendDSCStream::StartRecordingThread,(LPVOID)this,0,&m_CaptureThId);
m_DPFlags |= DPFLAG_STARTED_SEND;
DEBUGMSG (ZONE_DP, ("%s: Record threadid=%x,\r\n", _fx_, m_CaptureThId)); return DPR_SUCCESS; }
HRESULT SendDSCStream::Stop() { DWORD dwWait;
if(!(m_DPFlags & DPFLAG_STARTED_SEND)) { return DPR_SUCCESS; } m_ThreadFlags = m_ThreadFlags | DPTFLAG_STOP_SEND | DPTFLAG_STOP_RECORD ;
DEBUGMSG (ZONE_DP, ("SendDSCStream::Stop - Waiting for record thread to exit\r\n"));
if (m_hCapturingThread) { dwWait = WaitForSingleObject (m_hCapturingThread, INFINITE);
DEBUGMSG (ZONE_DP, ("SendDSCStream::Stop: Recording thread exited\r\n")); ASSERT(dwWait != WAIT_FAILED); CloseHandle(m_hCapturingThread); m_hCapturingThread = NULL; } m_DPFlags &= ~DPFLAG_STARTED_SEND; return DPR_SUCCESS; }
HRESULT STDMETHODCALLTYPE SendDSCStream::SetMaxBitrate(UINT uMaxBitrate) { return S_OK; }
HRESULT STDMETHODCALLTYPE SendDSCStream::QueryInterface(REFIID iid, void **ppVoid) { // resolve duplicate inheritance to the SendMediaStream;
if (iid == IID_IUnknown) { *ppVoid = (IUnknown*)((SendMediaStream*)this); } else if (iid == IID_IMediaChannel) { *ppVoid = (IMediaChannel*)((SendMediaStream *)this); } else if (iid == IID_IAudioChannel) { *ppVoid = (IAudioChannel*)this; } else if (iid == IID_IDTMFSend) { *ppVoid = (IDTMFSend*)this; } else { *ppVoid = NULL; return E_NOINTERFACE; } AddRef();
return S_OK;
}
ULONG STDMETHODCALLTYPE SendDSCStream::AddRef(void) { return InterlockedIncrement(&m_lRefCount); }
ULONG STDMETHODCALLTYPE SendDSCStream::Release(void) { LONG lRet;
lRet = InterlockedDecrement(&m_lRefCount);
if (lRet == 0) { delete this; return 0; }
else return lRet;
}
HRESULT STDMETHODCALLTYPE SendDSCStream::GetSignalLevel(UINT *pSignalStrength) { UINT uLevel; DWORD dwJammed;
if(!(m_DPFlags & DPFLAG_STARTED_SEND)) { uLevel = 0; } else { uLevel = m_AudioMonitor.GetSignalStrength();
if (m_bJammed) { uLevel = (2 << 16); // 0x0200
} else if (m_fSending) { uLevel |= (1 << 16); // 0x0100 + uLevel
} }
*pSignalStrength = uLevel; return S_OK;
return 0; }
HRESULT STDMETHODCALLTYPE SendDSCStream::GetProperty(DWORD dwProp, PVOID pBuf, LPUINT pcbBuf) { HRESULT hr = DPR_SUCCESS; RTP_STATS RTPStats; DWORD dwValue; UINT len = sizeof(DWORD); // most props are DWORDs
if (!pBuf || *pcbBuf < len) { *pcbBuf = len; return DPR_INVALID_PARAMETER; }
switch (dwProp) {
case PROP_SILENCE_LEVEL: *(DWORD *)pBuf = m_AudioMonitor.GetSilenceLevel(); break;
case PROP_DUPLEX_TYPE: if(m_bFullDuplex == TRUE) *(DWORD*)pBuf = DUPLEX_TYPE_FULL; else *(DWORD*)pBuf = DUPLEX_TYPE_HALF; break;
case PROP_RECORD_ON: *(DWORD *)pBuf = (m_DPFlags & DPFLAG_ENABLE_SEND) !=0; break;
case PROP_PAUSE_SEND: // To be determined
break;
case PROP_AUDIO_AUTOMIX: *(DWORD*)pBuf = m_bAutoMix; break;
case PROP_RECORD_DEVICE: *(DWORD *)pBuf = m_CaptureDevice; break;
default: hr = DPR_INVALID_PROP_ID; break; } return hr; }
HRESULT STDMETHODCALLTYPE SendDSCStream::SetProperty(DWORD dwProp, PVOID pBuf, UINT cbBuf) { DWORD dw; HRESULT hr = S_OK; if (cbBuf < sizeof (DWORD)) return DPR_INVALID_PARAMETER;
switch (dwProp) { case PROP_SILENCE_LEVEL: m_AudioMonitor.SetSilenceLevel(*(DWORD *)pBuf); break;
case DP_PROP_DUPLEX_TYPE: m_bFullDuplex = (*(DWORD*)pBuf != 0); break;
case PROP_AUDIO_AUTOMIX: m_bAutoMix = *(DWORD*)pBuf; break;
case PROP_RECORD_DEVICE: m_CaptureDevice = *(DWORD*)pBuf; RETAILMSG(("NAC: Setting default record device to %d", m_CaptureDevice)); break;
case PROP_RECORD_ON: { DWORD flag = DPFLAG_ENABLE_SEND ; if (*(DWORD *)pBuf) { m_DPFlags |= flag; // set the flag
Start(); } else { m_DPFlags &= ~flag; // clear the flag
Stop(); } RETAILMSG(("DSCStream: %s", *(DWORD*)pBuf ? "Enabling Stream":"Pausing stream")); break; }
default: return DPR_INVALID_PROP_ID; break; } return hr;
}
void SendDSCStream::EndSend() { return; }
HRESULT SendDSCStream::CreateAudioPackets(MEDIAPACKETINIT *pmpi) { int nIndex; HRESULT hr;
ReleaseAudioPackets();
for (nIndex = 0; nIndex < NUM_AUDIOPACKETS; nIndex++) { DBG_SAVE_FILE_LINE m_aPackets[nIndex] = new AudioPacket; if (m_aPackets[nIndex] == NULL) { return DPR_OUT_OF_MEMORY; }
pmpi->index = nIndex; hr = m_aPackets[nIndex]->Initialize(pmpi); if (FAILED(hr)) { ReleaseAudioPackets(); return hr; }
}
m_pAudioFilter->PrepareAudioPackets(m_aPackets, NUM_AUDIOPACKETS, AP_ENCODE);
return S_OK; }
HRESULT SendDSCStream::ReleaseAudioPackets() {
for (int nIndex = 0; nIndex < NUM_AUDIOPACKETS; nIndex++) { if (m_aPackets[nIndex]) { m_pAudioFilter->UnPrepareAudioPackets(&m_aPackets[nIndex], 1, AP_ENCODE); delete m_aPackets[nIndex]; m_aPackets[nIndex] = NULL; } } return S_OK; }
HRESULT SendDSCStream::CreateDSCBuffer() { GUID guid = GUID_NULL; HRESULT hr; DSCBUFFERDESC dsBufDesc; DWORD dwIndex; DSBPOSITIONNOTIFY *aNotifyPos; IDirectSoundNotify *pNotify = NULL;
if (!(m_DPFlags & DPFLAG_CONFIGURED_SEND)) { return E_FAIL; }
if (!m_pDSC) { ASSERT(m_pDSCBuffer==NULL);
DSC_Manager::MapWaveIdToGuid(m_CaptureDevice, &guid); hr = DSC_Manager::CreateInstance(&guid, &m_pDSC); if (FAILED(hr)) { return hr; } }
if (!m_pDSCBuffer) { ZeroMemory(&dsBufDesc, sizeof(dsBufDesc));
dsBufDesc.dwBufferBytes = m_dwDSCBufferSize; dsBufDesc.lpwfxFormat = &m_wfPCM; dsBufDesc.dwSize = sizeof(dsBufDesc);
hr = m_pDSC->CreateCaptureBuffer(&dsBufDesc, &m_pDSCBuffer, NULL); if (FAILED(hr)) { dsBufDesc.dwFlags = DSCBCAPS_WAVEMAPPED; hr = m_pDSC->CreateCaptureBuffer(&dsBufDesc, &m_pDSCBuffer, NULL); }
if (FAILED(hr)) { m_pDSC->Release(); m_pDSC = NULL; return hr; } else { // do the notification positions
DBG_SAVE_FILE_LINE aNotifyPos = new DSBPOSITIONNOTIFY[m_dwNumFrames]; for (dwIndex = 0; dwIndex < m_dwNumFrames; dwIndex++) { aNotifyPos[dwIndex].hEventNotify = m_hEvent; aNotifyPos[dwIndex].dwOffset = m_dwFrameSize * dwIndex; }
hr = m_pDSCBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&pNotify); if (SUCCEEDED(hr)) { hr = pNotify->SetNotificationPositions(m_dwNumFrames, aNotifyPos); } if (FAILED(hr)) { DEBUGMSG (ZONE_DP, ("Failed to set notification positions on DSC Buffer")); } } }
if (aNotifyPos) { delete [] aNotifyPos; } if (pNotify) { pNotify->Release(); }
return S_OK; }
HRESULT SendDSCStream::ReleaseDSCBuffer() { if (m_pDSCBuffer) { m_pDSCBuffer->Stop(); m_pDSCBuffer->Release(); m_pDSCBuffer = NULL; } if (m_pDSC) { m_pDSC->Release(); m_pDSC = NULL; }
return S_OK; }
// DTMF functions don't do anything if we aren't streaming
HRESULT __stdcall SendDSCStream::AddDigit(int nDigit) { IMediaChannel *pIMC = NULL; RecvMediaStream *pRecv = NULL; BOOL bIsStarted;
if ((!(m_DPFlags & DPFLAG_CONFIGURED_SEND)) || (m_pRTPSend==NULL)) { return DPR_NOT_CONFIGURED; }
bIsStarted = (m_DPFlags & DPFLAG_STARTED_SEND);
if (bIsStarted) { Stop(); }
m_pDTMF->AddDigitToQueue(nDigit); SendDTMF();
m_pDP->GetMediaChannelInterface(MCF_RECV | MCF_AUDIO, &pIMC); if (pIMC) { pRecv = static_cast<RecvMediaStream *> (pIMC); pRecv->DTMFBeep(); pIMC->Release(); }
if (bIsStarted) { Start(); }
return S_OK; }
// this function is ALMOST identical to SendAudioStream::SendDTMF
HRESULT __stdcall SendDSCStream::SendDTMF() { HRESULT hr=S_OK; MediaPacket *pPacket=NULL; ULONG uCount; UINT uBufferSize, uBytesSent; void *pBuffer; bool bMark = true; MMRESULT mmr; HANDLE hEvent = m_pDTMF->GetEvent(); UINT uTimerID; // since the stream is stopped, just grab any packet
// from the packet ring
pPacket = m_aPackets[0]; pPacket->GetDevData(&pBuffer, &uBufferSize);
timeBeginPeriod(5); ResetEvent(hEvent); uTimerID = timeSetEvent(m_dwFrameTimeMS-1, 5, (LPTIMECALLBACK )hEvent, 0, TIME_CALLBACK_EVENT_SET|TIME_PERIODIC);
hr = m_pDTMF->ReadFromQueue((BYTE*)pBuffer, uBufferSize);
while (SUCCEEDED(hr)) { // there should be only 1 tone in the queue (it can handle more)
// so assume we only need to set the mark bit on the first packet
pPacket->m_fMark = bMark; bMark = false;
pPacket->SetProp(MP_PROP_TIMESTAMP, m_SendTimestamp); m_SendTimestamp += m_dwSamplesPerFrame;
pPacket->SetState (MP_STATE_RECORDED);
// SendPacket will also compress
SendPacket((AudioPacket*)pPacket);
pPacket->m_fMark=false; pPacket->SetState(MP_STATE_RESET);
hr = m_pDTMF->ReadFromQueue((BYTE*)pBuffer, uBufferSize);
// so that we don't overload the receive jitter buffer on the remote
// side, sleep a few milliseconds between sending packets
if (SUCCEEDED(hr)) { WaitForSingleObject(hEvent, m_dwFrameTimeMS); ResetEvent(hEvent); } }
timeKillEvent(uTimerID); timeEndPeriod(5); return S_OK; }
HRESULT __stdcall SendDSCStream::ResetDTMF() { if(!(m_DPFlags & DPFLAG_STARTED_SEND)) { return S_OK; }
return m_pDTMF->ClearQueue(); }
|