1085 lines
21 KiB
C++
1085 lines
21 KiB
C++
#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();
|
||
}
|
||
|
||
|