You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
934 lines
22 KiB
934 lines
22 KiB
/*
|
|
DATAPUMP.C
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include "confreg.h"
|
|
#include "mixer.h"
|
|
#include "dscStream.h"
|
|
|
|
extern UINT g_MinDSEmulAudioDelayMs; // minimum millisecs of introduced playback delay (DirectSound on emulated drivers)
|
|
extern UINT g_MinWaveAudioDelayMs; // minimum millisecs of introduced playback delay (Wave)
|
|
extern UINT g_MaxAudioDelayMs; // maximum milliesecs of introduced playback delay
|
|
extern UINT g_AudioPacketDurationMs; // preferred packet duration
|
|
|
|
extern int g_wavein_prepare, g_waveout_prepare;
|
|
extern int g_videoin_prepare, g_videoout_prepare;
|
|
|
|
#define RSVP_KEY TEXT("RSVP")
|
|
|
|
HANDLE g_hEventHalfDuplex = NULL;
|
|
|
|
HWND DataPump::m_hAppWnd = NULL;
|
|
HINSTANCE DataPump::m_hAppInst = NULL;
|
|
|
|
|
|
HRESULT WINAPI CreateStreamProvider(IMediaChannelBuilder **lplpSP)
|
|
{
|
|
DataPump * pDataPump;
|
|
if(!lplpSP)
|
|
return DPR_INVALID_PARAMETER;
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
pDataPump = new DataPump;
|
|
if(NULL == pDataPump)
|
|
return DPR_OUT_OF_MEMORY;
|
|
|
|
// the refcount of DataPump is 1. Don't call pDataPump->QueryInterface(),
|
|
// just do what QueryInterface() would do except don't increment refcount
|
|
*lplpSP = (IMediaChannelBuilder *)pDataPump;
|
|
return hrSuccess;
|
|
}
|
|
|
|
|
|
DataPump::DataPump(void)
|
|
:m_uRef(1)
|
|
{
|
|
ClearStruct( &m_Audio );
|
|
ClearStruct( &m_Video );
|
|
InitializeCriticalSection(&m_crs);
|
|
|
|
// Create performance counters
|
|
InitCountersAndReports();
|
|
}
|
|
|
|
DataPump::~DataPump(void)
|
|
{
|
|
ReleaseResources();
|
|
|
|
WSACleanup();
|
|
DeleteCriticalSection(&m_crs);
|
|
|
|
// We're done with performance counters
|
|
DoneCountersAndReports();
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::Initialize(HWND hWnd, HINSTANCE hInst)
|
|
{
|
|
HRESULT hr = DPR_OUT_OF_MEMORY;
|
|
FX_ENTRY ("DP::Init")
|
|
WSADATA WSAData;
|
|
int status;
|
|
BOOL fDisableWS2;
|
|
UINT uMinDelay;
|
|
TCHAR *szKey = NACOBJECT_KEY TEXT("\\") RSVP_KEY;
|
|
RegEntry reRSVP(szKey, HKEY_LOCAL_MACHINE, FALSE);
|
|
|
|
if((NULL == hWnd) || (NULL == hInst))
|
|
goto InitError;
|
|
|
|
m_hAppInst = hInst;
|
|
m_hAppWnd = hWnd;
|
|
|
|
status = WSAStartup(MAKEWORD(1,1), &WSAData);
|
|
if(status !=0)
|
|
{
|
|
ERRORMESSAGE(("CNac::Init:WSAStartup failed\r\n"));
|
|
goto InitError;
|
|
}
|
|
|
|
// Introduce scope to allow creation of object after goto statements
|
|
{
|
|
|
|
// get settings from registry
|
|
RegEntry reNac(szRegInternetPhone TEXT("\\") szRegInternetPhoneNac,
|
|
HKEY_LOCAL_MACHINE,
|
|
FALSE,
|
|
KEY_READ);
|
|
|
|
g_MaxAudioDelayMs = reNac.GetNumberIniStyle(TEXT ("MaxAudioDelayMs"), g_MaxAudioDelayMs);
|
|
|
|
uMinDelay = reNac.GetNumberIniStyle(TEXT ("MinAudioDelayMs"), 0);
|
|
|
|
if (uMinDelay != 0)
|
|
{
|
|
g_MinWaveAudioDelayMs = uMinDelay;
|
|
g_MinDSEmulAudioDelayMs = uMinDelay;
|
|
}
|
|
|
|
fDisableWS2 = reNac.GetNumberIniStyle(TEXT ("DisableWinsock2"), 0);
|
|
|
|
}
|
|
#ifdef OLDSTUFF
|
|
// to be safe, only try loading WS2_32 if WSOCK32 is passing
|
|
// thru to it. Once we make sure that we link to the same DLL for all
|
|
// Winsock calls to a socket, this check can possibly be removed.
|
|
if (LOBYTE(WSAData.wHighVersion) >= 2 && !fDisableWS2)
|
|
TryLoadWinsock2();
|
|
#endif
|
|
// Initialize data (should be in constructor)
|
|
|
|
g_hEventHalfDuplex = CreateEvent (NULL, FALSE, TRUE, __TEXT ("AVC:HalfDuplex"));
|
|
if (g_hEventHalfDuplex == NULL)
|
|
{
|
|
DEBUGMSG (ZONE_DP, ("%s: CreateEvent failed, LastErr=%lu\r\n", _fx_, GetLastError ()));
|
|
hr = DPR_CANT_CREATE_EVENT;
|
|
return hr;
|
|
}
|
|
|
|
// Initialize QoS. If it fails, that's Ok, we'll do without it.
|
|
// No need to set the resource ourselves, this now done by the UI
|
|
hr = CreateQoS (NULL, IID_IQoS, (void **)&m_pIQoS);
|
|
if (hr != DPR_SUCCESS)
|
|
m_pIQoS = (LPIQOS)NULL;
|
|
|
|
m_bDisableRSVP = reRSVP.GetNumber("DisableRSVP", FALSE);
|
|
|
|
|
|
LogInit(); // Initialize log
|
|
|
|
//No receive channels yet
|
|
m_nReceivers=0;
|
|
|
|
// IVideoDevice initialize
|
|
m_uVideoCaptureId = -1; // (VIDEO_MAPPER)
|
|
|
|
// IAudioDevice initialize
|
|
m_uWaveInID = WAVE_MAPPER;
|
|
m_uWaveOutID = WAVE_MAPPER;
|
|
m_bFullDuplex = FALSE;
|
|
m_uSilenceLevel = 1000; // automatic silence detection
|
|
m_bAutoMix = FALSE;
|
|
m_bDirectSound = FALSE;
|
|
|
|
|
|
return DPR_SUCCESS;
|
|
|
|
InitError:
|
|
ERRORMESSAGE( ("DataPump::Initialize: exit, hr=0x%lX\r\n", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
DataPump::CreateMediaChannel( UINT flags, IMediaChannel **ppIMC)
|
|
{
|
|
IUnknown *pUnkOuter = NULL;
|
|
IMediaChannel *pStream = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// try to be consistant about which parent classes we cast to
|
|
|
|
*ppIMC = NULL;
|
|
|
|
|
|
if (flags & MCF_AUDIO)
|
|
{
|
|
if ((flags & MCF_SEND) && !m_Audio.pSendStream)
|
|
{
|
|
if (m_bDirectSound && (DSC_Manager::Initialize() == S_OK))
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pStream = (IMediaChannel*)(SendMediaStream*)new SendDSCStream;
|
|
}
|
|
else
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pStream = (IMediaChannel*)(SendMediaStream*)new SendAudioStream;
|
|
}
|
|
|
|
}
|
|
else if ((flags & MCF_RECV) && !m_Audio.pRecvStream)
|
|
{
|
|
if (m_bDirectSound && (DirectSoundMgr::Initialize() == S_OK))
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pStream = (IMediaChannel*)(RecvMediaStream*)new RecvDSAudioStream;
|
|
}
|
|
else
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pStream = (IMediaChannel*)(RecvMediaStream*)new RecvAudioStream;
|
|
}
|
|
}
|
|
}
|
|
else if (flags & MCF_VIDEO)
|
|
{
|
|
if ((flags & MCF_SEND) && !m_Video.pSendStream)
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pStream = (IMediaChannel*)(SendMediaStream*) new SendVideoStream;
|
|
}
|
|
else if ((flags & MCF_RECV) && !m_Video.pRecvStream)
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pStream = (IMediaChannel*)(RecvMediaStream*) new RecvVideoStream;
|
|
}
|
|
}
|
|
else
|
|
hr = E_INVALIDARG;
|
|
|
|
if (pStream != NULL) {
|
|
// need to inc the refCount of the object
|
|
pStream->AddRef();
|
|
|
|
hr = (flags & MCF_SEND) ?
|
|
((SendMediaStream *)pStream)->Initialize( this)
|
|
: ((RecvMediaStream *)pStream)->Initialize(this);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pStream->QueryInterface(IID_IMediaChannel, (void **)ppIMC);
|
|
}
|
|
if (hr == S_OK)
|
|
{
|
|
AddMediaChannel(flags, pStream);
|
|
}
|
|
|
|
|
|
// calling to the IVideoDevice and IAudioDevice methods
|
|
// prior to creating the corresponding channel object
|
|
// require when they get created
|
|
|
|
// video only needs it's device ID set
|
|
if ((flags & MCF_SEND) && (flags & MCF_VIDEO))
|
|
{
|
|
SetCurrCapDevID(m_uVideoCaptureId);
|
|
}
|
|
|
|
// audio streams need several properties set
|
|
if (flags & MCF_AUDIO)
|
|
{
|
|
if (flags & MCF_SEND)
|
|
{
|
|
SetSilenceLevel(m_uSilenceLevel);
|
|
SetAutoMix(m_bAutoMix);
|
|
SetRecordID(m_uWaveInID);
|
|
}
|
|
else if (flags & MCF_RECV)
|
|
{
|
|
SetPlaybackID(m_uWaveOutID);
|
|
}
|
|
SetStreamDuplex(pStream, m_bFullDuplex);
|
|
}
|
|
|
|
// to avoid a circular ref-count,
|
|
// dont keep a hard reference to MediaChannel objects
|
|
// MediaChannel will call RemoveMediaChannel before it goes away..
|
|
pStream->Release();
|
|
pStream = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DataPump::SetStreamEventObj(IStreamEventNotify *pNotify)
|
|
{
|
|
|
|
EnterCriticalSection(&m_crs);
|
|
|
|
if (m_pTEP)
|
|
{
|
|
delete m_pTEP;
|
|
m_pTEP = NULL;
|
|
}
|
|
|
|
if (pNotify)
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
m_pTEP = new ThreadEventProxy(pNotify, m_hAppInst);
|
|
}
|
|
|
|
LeaveCriticalSection(&m_crs);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
// this function gets called by the stream threads when an event occurs
|
|
STDMETHODIMP DataPump::StreamEvent(UINT uDirection, UINT uMediaType,
|
|
UINT uEventType, UINT uSubCode)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
EnterCriticalSection(&m_crs);
|
|
|
|
if (m_pTEP)
|
|
{
|
|
bRet = m_pTEP->ThreadEvent(uDirection, uMediaType, uEventType, uSubCode);
|
|
}
|
|
|
|
LeaveCriticalSection(&m_crs);
|
|
|
|
return bRet ? DPR_INVALID_PARAMETER : DPR_SUCCESS;
|
|
}
|
|
|
|
|
|
void
|
|
DataPump::AddMediaChannel(UINT flags, IMediaChannel *pMediaChannel)
|
|
{
|
|
EnterCriticalSection(&m_crs);
|
|
if (flags & MCF_SEND)
|
|
{
|
|
SendMediaStream *pS = static_cast<SendMediaStream *> (pMediaChannel);
|
|
if (flags & MCF_AUDIO)
|
|
m_Audio.pSendStream = pS;
|
|
else if (flags & MCF_VIDEO)
|
|
m_Video.pSendStream = pS;
|
|
}
|
|
else if (flags & MCF_RECV)
|
|
{
|
|
RecvMediaStream *pR = static_cast<RecvMediaStream *> (pMediaChannel);
|
|
if (flags & MCF_AUDIO)
|
|
m_Audio.pRecvStream = pR;
|
|
else if (flags & MCF_VIDEO)
|
|
m_Video.pRecvStream = pR;
|
|
}
|
|
LeaveCriticalSection(&m_crs);
|
|
}
|
|
|
|
void
|
|
DataPump::RemoveMediaChannel(UINT flags, IMediaChannel *pMediaChannel)
|
|
{
|
|
EnterCriticalSection(&m_crs);
|
|
if (flags & MCF_SEND)
|
|
{
|
|
if (flags & MCF_AUDIO)
|
|
{
|
|
ASSERT(pMediaChannel == m_Audio.pSendStream);
|
|
if (pMediaChannel == m_Audio.pSendStream)
|
|
m_Audio.pSendStream = NULL;
|
|
}
|
|
else if (flags & MCF_VIDEO)
|
|
{
|
|
ASSERT(pMediaChannel == m_Video.pSendStream);
|
|
m_Video.pSendStream = NULL;
|
|
}
|
|
}
|
|
else if (flags & MCF_RECV)
|
|
{
|
|
if (flags & MCF_AUDIO)
|
|
{
|
|
ASSERT(pMediaChannel == m_Audio.pRecvStream);
|
|
m_Audio.pRecvStream = NULL;
|
|
}
|
|
else if (flags & MCF_VIDEO)
|
|
{
|
|
ASSERT(pMediaChannel == m_Video.pRecvStream);
|
|
m_Video.pRecvStream = NULL;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_crs);
|
|
|
|
}
|
|
|
|
// called by Record Thread and Receive Thread, usually to get the
|
|
// opposite channel
|
|
HRESULT DataPump::GetMediaChannelInterface( UINT flags, IMediaChannel **ppI)
|
|
{
|
|
// extern IID IID_IMediaChannel;
|
|
|
|
IMediaChannel *pStream = NULL;
|
|
|
|
HRESULT hr;
|
|
EnterCriticalSection(&m_crs);
|
|
if (flags & MCF_AUDIO) {
|
|
if (flags & MCF_SEND) {
|
|
pStream = m_Audio.pSendStream;
|
|
} else if (flags & MCF_RECV) {
|
|
pStream = m_Audio.pRecvStream;
|
|
}
|
|
}
|
|
else if (flags & MCF_VIDEO) {
|
|
if (flags & MCF_SEND) {
|
|
pStream = m_Video.pSendStream;
|
|
} else if (flags & MCF_RECV) {
|
|
pStream = m_Video.pRecvStream;
|
|
}
|
|
} else
|
|
hr = DPR_INVALID_PARAMETER;
|
|
if (pStream) {
|
|
// need to inc the refCount of the object
|
|
hr = (pStream)->QueryInterface(IID_IMediaChannel, (PVOID *)ppI);
|
|
} else
|
|
hr = E_NOINTERFACE;
|
|
LeaveCriticalSection(&m_crs);
|
|
return hr;
|
|
}
|
|
|
|
|
|
DWORD __stdcall StartDPRecvThread(PVOID pVoid)
|
|
{
|
|
DataPump *pDP = (DataPump*)pVoid;
|
|
return pDP->CommonWS2RecvThread();
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP DataPump::QueryInterface( REFIID iid, void ** ppvObject)
|
|
{
|
|
// this breaks the rules for the official COM QueryInterface because
|
|
// the interfaces that are queried for are not necessarily real COM
|
|
// interfaces. The reflexive property of QueryInterface would be broken in
|
|
// that case.
|
|
HRESULT hr = E_NOINTERFACE;
|
|
if(!ppvObject)
|
|
return hr;
|
|
|
|
*ppvObject = NULL;
|
|
if(iid == IID_IUnknown)// satisfy symmetric property of QI
|
|
{
|
|
*ppvObject = this;
|
|
hr = hrSuccess;
|
|
AddRef();
|
|
}
|
|
else if(iid == IID_IMediaChannelBuilder)
|
|
{
|
|
*ppvObject = (IMediaChannelBuilder *)this;
|
|
hr = hrSuccess;
|
|
AddRef();
|
|
}
|
|
else if (iid == IID_IVideoDevice)
|
|
{
|
|
*ppvObject = (IVideoDevice *)this;
|
|
hr = hrSuccess;
|
|
AddRef();
|
|
}
|
|
|
|
else if (iid == IID_IAudioDevice)
|
|
{
|
|
*ppvObject = (IAudioDevice*)this;
|
|
hr = hrSuccess;
|
|
AddRef();
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
ULONG DataPump::AddRef()
|
|
{
|
|
m_uRef++;
|
|
return m_uRef;
|
|
}
|
|
|
|
ULONG DataPump::Release()
|
|
{
|
|
m_uRef--;
|
|
if(m_uRef == 0)
|
|
{
|
|
m_hAppWnd = NULL;
|
|
m_hAppInst = NULL;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_uRef;
|
|
}
|
|
|
|
|
|
void
|
|
DataPump::ReleaseResources()
|
|
{
|
|
FX_ENTRY ("DP::ReleaseResources")
|
|
|
|
#ifdef DEBUG
|
|
if (m_Audio.pSendStream)
|
|
ERRORMESSAGE(("%s: Audio Send stream still around => Ref count LEAK!\n", _fx_));
|
|
if (m_Audio.pRecvStream)
|
|
ERRORMESSAGE(("%s: Audio Recv stream still around => Ref count LEAK!\n", _fx_));
|
|
if (m_Video.pSendStream)
|
|
ERRORMESSAGE(("%s: Video Send stream still around => Ref count LEAK!\n", _fx_));
|
|
if (m_Video.pRecvStream)
|
|
ERRORMESSAGE(("%s: Video Recv stream still around => Ref count LEAK!\n", _fx_));
|
|
#endif
|
|
|
|
// close debug log
|
|
LogClose();
|
|
|
|
// Free QoS resources
|
|
if (m_pIQoS)
|
|
{
|
|
m_pIQoS->Release();
|
|
m_pIQoS = (LPIQOS)NULL;
|
|
}
|
|
|
|
// Close the receive and transmit streams
|
|
if (g_hEventHalfDuplex)
|
|
{
|
|
CloseHandle (g_hEventHalfDuplex);
|
|
g_hEventHalfDuplex = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
HRESULT DataPump::SetStreamDuplex(IMediaChannel *pStream, BOOL bFullDuplex)
|
|
{
|
|
BOOL fOn = (pStream->GetState() == MSSTATE_STARTED);
|
|
BOOL bStreamFullDuplex;
|
|
UINT uSize = sizeof(BOOL);
|
|
|
|
pStream->GetProperty(PROP_DUPLEX_TYPE, &bStreamFullDuplex, &uSize);
|
|
|
|
if (bStreamFullDuplex != bFullDuplex)
|
|
{
|
|
if (fOn)
|
|
{
|
|
pStream->Stop();
|
|
}
|
|
|
|
pStream->SetProperty(DP_PROP_DUPLEX_TYPE, &bFullDuplex, sizeof(BOOL));
|
|
|
|
if (fOn)
|
|
{
|
|
pStream->Start();
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT __stdcall DataPump::SetDuplex(BOOL bFullDuplex)
|
|
{
|
|
IMediaChannel *pS = m_Audio.pSendStream;
|
|
IMediaChannel *pR = m_Audio.pRecvStream;
|
|
IMediaChannel *pStream;
|
|
BOOL fPlayOn = FALSE;
|
|
BOOL fRecOn = FALSE;
|
|
UINT uSize;
|
|
BOOL bRecDuplex, bPlayDuplex;
|
|
|
|
m_bFullDuplex = bFullDuplex ? TRUE : FALSE;
|
|
|
|
UPDATE_REPORT_ENTRY(g_prptSystemSettings, (m_bFullDuplex) ? 1 : 0, REP_SYS_AUDIO_DUPLEX);
|
|
RETAILMSG(("NAC: Audio Duplex Type: %s",(m_bFullDuplex) ? "Full Duplex" : "Half Duplex"));
|
|
|
|
|
|
// no streams ? No problem.
|
|
if ((pS == NULL) && (pR == NULL))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// only one stream
|
|
if ((pS || pR) && !(pS && pR))
|
|
{
|
|
if (pS)
|
|
pStream = pS;
|
|
else
|
|
pStream = pR;
|
|
|
|
return SetStreamDuplex(pStream, m_bFullDuplex);
|
|
}
|
|
|
|
|
|
// assert - pS && pR
|
|
|
|
// both streams exist
|
|
|
|
// try to avoid the whole start/stop sequence if the duplex
|
|
// is the same
|
|
uSize=sizeof(BOOL);
|
|
pR->GetProperty(PROP_DUPLEX_TYPE, &bRecDuplex, &uSize);
|
|
uSize=sizeof(BOOL);
|
|
pS->GetProperty(PROP_DUPLEX_TYPE, &bPlayDuplex, &uSize);
|
|
|
|
if ( (bPlayDuplex == m_bFullDuplex) &&
|
|
(bRecDuplex == m_bFullDuplex))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// save the old thread flags
|
|
fPlayOn = (pR->GetState() == MSSTATE_STARTED);
|
|
fRecOn = (pS->GetState() == MSSTATE_STARTED);
|
|
|
|
// Ensure the record and playback threads are stopped
|
|
pR->Stop();
|
|
pS->Stop();
|
|
|
|
SetStreamDuplex(pR, m_bFullDuplex);
|
|
SetStreamDuplex(pS, m_bFullDuplex);
|
|
|
|
// Resume the record/playback
|
|
// Try to let play start before record - DirectS and SB16 prefer that!
|
|
if (fPlayOn)
|
|
{
|
|
pR->Start();
|
|
}
|
|
|
|
if (fRecOn)
|
|
{
|
|
pS->Start();
|
|
}
|
|
|
|
return DPR_SUCCESS;
|
|
}
|
|
|
|
#define LONGTIME 60000 // 60 seconds
|
|
|
|
// utility function to synchronously communicate a
|
|
// a state change to the recv thread
|
|
HRESULT DataPump::RecvThreadMessage(UINT msg, RecvMediaStream *pMS)
|
|
{
|
|
BOOL fSignaled;
|
|
DWORD dwWaitStatus;
|
|
HANDLE handle;
|
|
// Unfortunately cant use PostThreadMessage to signal the thread
|
|
// because it doesnt have a message loop
|
|
m_pCurRecvStream = pMS;
|
|
m_CurRecvMsg = msg;
|
|
fSignaled = SetEvent(m_hRecvThreadSignalEvent);
|
|
|
|
|
|
if (fSignaled) {
|
|
|
|
handle = (msg == MSG_EXIT_RECV ? m_hRecvThread : m_hRecvThreadAckEvent);
|
|
dwWaitStatus = WaitForSingleObject(handle, LONGTIME);
|
|
ASSERT(dwWaitStatus == WAIT_OBJECT_0);
|
|
if (dwWaitStatus != WAIT_OBJECT_0)
|
|
return GetLastError();
|
|
} else
|
|
return GetLastError();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// start receiving on this stream
|
|
// will create the receive thread if necessary.
|
|
HRESULT
|
|
DataPump::StartReceiving(RecvMediaStream *pMS)
|
|
{
|
|
DWORD dwWaitStatus;
|
|
FX_ENTRY("DP::StartReceiving")
|
|
// one more stream
|
|
m_nReceivers++;
|
|
if (!m_hRecvThread) {
|
|
ASSERT(m_nReceivers==1);
|
|
ASSERT(!m_hRecvThreadAckEvent);
|
|
//Use this for thread event notifications. I.e. Video started/stopped, audio stopped, et al.
|
|
//m_hRecvThreadChangeEvent=CreateEvent (NULL,FALSE,FALSE,NULL);
|
|
//create the stopping sync event
|
|
m_hRecvThreadAckEvent=CreateEvent (NULL,FALSE,FALSE,NULL);
|
|
m_hRecvThreadSignalEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
|
|
m_hRecvThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)StartDPRecvThread,(PVOID)this,0,&m_RecvThId);
|
|
DEBUGMSG(ZONE_DP,("%s: RecvThread Id=%x\n",_fx_,m_RecvThId));
|
|
// thread will signal event soon as its message loop is ready
|
|
dwWaitStatus = WaitForSingleObject(m_hRecvThreadAckEvent, LONGTIME);
|
|
ASSERT(dwWaitStatus == WAIT_OBJECT_0);
|
|
}
|
|
|
|
//Tell the recv Thread to start receiving on this MediaStream
|
|
return RecvThreadMessage(MSG_START_RECV,pMS);
|
|
|
|
|
|
}
|
|
|
|
// Stop receiving on the stream
|
|
// will stop the receive thread if necessary
|
|
HRESULT
|
|
DataPump::StopReceiving(RecvMediaStream *pMS)
|
|
{
|
|
HANDLE rgh[2];
|
|
ASSERT(m_nReceivers > 0);
|
|
ASSERT(m_hRecvThread);
|
|
if (m_nReceivers > 0)
|
|
m_nReceivers--;
|
|
|
|
RecvThreadMessage(MSG_STOP_RECV, pMS);
|
|
|
|
if (!m_nReceivers && m_hRecvThread) {
|
|
// kill the receive thread
|
|
RecvThreadMessage(MSG_EXIT_RECV,NULL);
|
|
|
|
CloseHandle(m_hRecvThread);
|
|
CloseHandle(m_hRecvThreadAckEvent);
|
|
m_hRecvThread = NULL;
|
|
m_hRecvThreadAckEvent = NULL;
|
|
if (m_hRecvThreadSignalEvent) {
|
|
CloseHandle(m_hRecvThreadSignalEvent);
|
|
m_hRecvThreadSignalEvent = NULL;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// IVideoDevice Methods
|
|
//
|
|
|
|
// Capture device methods
|
|
|
|
// Gets the number of enabled capture devices
|
|
// Returns -1L on error
|
|
HRESULT __stdcall DataPump::GetNumCapDev()
|
|
{
|
|
FINDCAPTUREDEVICE fcd;
|
|
|
|
// scan for broken or unplugged devices
|
|
FindFirstCaptureDevice(&fcd, NULL);
|
|
|
|
return (GetNumCaptureDevices());
|
|
}
|
|
|
|
// Gets the max size of the captuire device name
|
|
// Returns -1L on error
|
|
HRESULT __stdcall DataPump::GetMaxCapDevNameLen()
|
|
{
|
|
return (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION);
|
|
}
|
|
|
|
// Enum list of enabled capture devices
|
|
// Fills up 1st buffer with device IDs, 2nd buffer with device names
|
|
// Third parameter is the max number of devices to enum
|
|
// Returns number of devices enum-ed
|
|
HRESULT __stdcall DataPump::EnumCapDev(DWORD *pdwCapDevIDs, TCHAR *pszCapDevNames, DWORD dwNumCapDev)
|
|
{
|
|
FINDCAPTUREDEVICE fcd;
|
|
DWORD dwNumCapDevFound = 0;
|
|
|
|
fcd.dwSize = sizeof (FINDCAPTUREDEVICE);
|
|
if (FindFirstCaptureDevice(&fcd, NULL))
|
|
{
|
|
do
|
|
{
|
|
pdwCapDevIDs[dwNumCapDevFound] = fcd.nDeviceIndex;
|
|
|
|
// Build device name out of the capture device strings
|
|
if (fcd.szDeviceDescription && fcd.szDeviceDescription[0] != '\0')
|
|
lstrcpy(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), fcd.szDeviceDescription);
|
|
else
|
|
lstrcpy(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), fcd.szDeviceName);
|
|
if (fcd.szDeviceVersion && fcd.szDeviceVersion[0] != '\0')
|
|
{
|
|
lstrcat(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), ", ");
|
|
lstrcat(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), fcd.szDeviceVersion);
|
|
}
|
|
dwNumCapDevFound++;
|
|
} while ((dwNumCapDevFound < dwNumCapDev) && FindNextCaptureDevice(&fcd));
|
|
}
|
|
|
|
return (dwNumCapDevFound);
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::GetCurrCapDevID()
|
|
{
|
|
UINT uCapID;
|
|
UINT uSize = sizeof(UINT);
|
|
|
|
// even though we know the value of the last call
|
|
// to SetCurrCapDevID, the stream may have resulted in using
|
|
// wave_mapper (-1). We want to be able to return -1, if this
|
|
// is the case. However, the channel objects don't do this yet.
|
|
// (they still return the same value as m_uVideoCaptureId)
|
|
|
|
if (m_Video.pSendStream)
|
|
{
|
|
m_Video.pSendStream->GetProperty(PROP_CAPTURE_DEVICE, &uCapID, &uSize);
|
|
#ifdef DEBUG
|
|
if (uCapID != m_uVideoCaptureId)
|
|
{
|
|
DEBUGMSG(ZONE_DP,("Video capture stream had to revert to MAPPER or some other device"));
|
|
}
|
|
#endif
|
|
return uCapID;
|
|
}
|
|
|
|
return m_uVideoCaptureId;
|
|
|
|
}
|
|
|
|
|
|
HRESULT __stdcall DataPump::SetCurrCapDevID(int nCapDevID)
|
|
{
|
|
m_uVideoCaptureId = (UINT)nCapDevID;
|
|
|
|
if (m_Video.pSendStream)
|
|
{
|
|
m_Video.pSendStream->SetProperty(PROP_CAPTURE_DEVICE, &m_uVideoCaptureId, sizeof(m_uVideoCaptureId));
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
// IAudioDevice methods
|
|
HRESULT __stdcall DataPump::GetRecordID(UINT *puWaveDevID)
|
|
{
|
|
*puWaveDevID = m_uWaveInID;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::SetRecordID(UINT uWaveDevID)
|
|
{
|
|
m_uWaveInID = uWaveDevID;
|
|
|
|
if (m_Audio.pSendStream)
|
|
{
|
|
m_Audio.pSendStream->SetProperty(PROP_RECORD_DEVICE, &m_uWaveInID, sizeof(m_uWaveInID));
|
|
}
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
HRESULT __stdcall DataPump::GetPlaybackID(UINT *puWaveDevID)
|
|
{
|
|
// like video, the audio device may have resorted to using
|
|
// WAVE_MAPPER. We'd like to be able to detect that
|
|
|
|
*puWaveDevID = m_uWaveOutID;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::SetPlaybackID(UINT uWaveDevID)
|
|
{
|
|
m_uWaveOutID = uWaveDevID;
|
|
|
|
if (m_Audio.pRecvStream)
|
|
{
|
|
m_Audio.pRecvStream->SetProperty(PROP_PLAYBACK_DEVICE, &m_uWaveOutID, sizeof(m_uWaveOutID));
|
|
}
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::GetSilenceLevel(UINT *puLevel)
|
|
{
|
|
*puLevel = m_uSilenceLevel;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT __stdcall DataPump::SetSilenceLevel(UINT uLevel)
|
|
{
|
|
m_uSilenceLevel = uLevel;
|
|
|
|
if (m_Audio.pSendStream)
|
|
{
|
|
m_Audio.pSendStream->SetProperty(PROP_SILENCE_LEVEL, &m_uSilenceLevel, sizeof(m_uSilenceLevel));
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT __stdcall DataPump::GetDuplex(BOOL *pbFullDuplex)
|
|
{
|
|
*pbFullDuplex = m_bFullDuplex;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT __stdcall DataPump::GetMixer(HWND hwnd, BOOL bPlayback, IMixer **ppMixer)
|
|
{
|
|
CMixerDevice *pMixerDevice = NULL;
|
|
DWORD dwFlags;
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
// unfortunately, trying to create a mixer when WAVE_MAPPER
|
|
// has been specified as the device ID results in a mixer
|
|
// that doesn't work on Win95.
|
|
|
|
*ppMixer = NULL;
|
|
|
|
if ((bPlayback) && (m_uWaveOutID != WAVE_MAPPER))
|
|
{
|
|
pMixerDevice = CMixerDevice::GetMixerForWaveDevice(hwnd, m_uWaveOutID, MIXER_OBJECTF_WAVEOUT);
|
|
}
|
|
else if (m_uWaveInID != WAVE_MAPPER)
|
|
{
|
|
pMixerDevice = CMixerDevice::GetMixerForWaveDevice(hwnd, m_uWaveInID, MIXER_OBJECTF_WAVEIN);
|
|
}
|
|
|
|
if (pMixerDevice)
|
|
{
|
|
hr = pMixerDevice->QueryInterface(IID_IMixer, (void**)ppMixer);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT __stdcall DataPump::GetAutoMix(BOOL *pbAutoMix)
|
|
{
|
|
*pbAutoMix = m_bAutoMix;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::SetAutoMix(BOOL bAutoMix)
|
|
{
|
|
m_bAutoMix = bAutoMix;
|
|
if (m_Audio.pSendStream)
|
|
{
|
|
m_Audio.pSendStream->SetProperty(PROP_AUDIO_AUTOMIX, &m_bAutoMix, sizeof(m_bAutoMix));
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::GetDirectSound(BOOL *pbDS)
|
|
{
|
|
*pbDS = m_bDirectSound;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall DataPump::SetDirectSound(BOOL bDS)
|
|
{
|
|
m_bDirectSound = bDS;
|
|
return S_OK;
|
|
}
|