Leaked source code of windows server 2003
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

  1. /*
  2. DATAPUMP.C
  3. */
  4. #include "precomp.h"
  5. #include "confreg.h"
  6. #include "mixer.h"
  7. #include "dscStream.h"
  8. extern UINT g_MinDSEmulAudioDelayMs; // minimum millisecs of introduced playback delay (DirectSound on emulated drivers)
  9. extern UINT g_MinWaveAudioDelayMs; // minimum millisecs of introduced playback delay (Wave)
  10. extern UINT g_MaxAudioDelayMs; // maximum milliesecs of introduced playback delay
  11. extern UINT g_AudioPacketDurationMs; // preferred packet duration
  12. extern int g_wavein_prepare, g_waveout_prepare;
  13. extern int g_videoin_prepare, g_videoout_prepare;
  14. #define RSVP_KEY TEXT("RSVP")
  15. HANDLE g_hEventHalfDuplex = NULL;
  16. HWND DataPump::m_hAppWnd = NULL;
  17. HINSTANCE DataPump::m_hAppInst = NULL;
  18. HRESULT WINAPI CreateStreamProvider(IMediaChannelBuilder **lplpSP)
  19. {
  20. DataPump * pDataPump;
  21. if(!lplpSP)
  22. return DPR_INVALID_PARAMETER;
  23. DBG_SAVE_FILE_LINE
  24. pDataPump = new DataPump;
  25. if(NULL == pDataPump)
  26. return DPR_OUT_OF_MEMORY;
  27. // the refcount of DataPump is 1. Don't call pDataPump->QueryInterface(),
  28. // just do what QueryInterface() would do except don't increment refcount
  29. *lplpSP = (IMediaChannelBuilder *)pDataPump;
  30. return hrSuccess;
  31. }
  32. DataPump::DataPump(void)
  33. :m_uRef(1)
  34. {
  35. ClearStruct( &m_Audio );
  36. ClearStruct( &m_Video );
  37. InitializeCriticalSection(&m_crs);
  38. // Create performance counters
  39. InitCountersAndReports();
  40. }
  41. DataPump::~DataPump(void)
  42. {
  43. ReleaseResources();
  44. WSACleanup();
  45. DeleteCriticalSection(&m_crs);
  46. // We're done with performance counters
  47. DoneCountersAndReports();
  48. }
  49. HRESULT __stdcall DataPump::Initialize(HWND hWnd, HINSTANCE hInst)
  50. {
  51. HRESULT hr = DPR_OUT_OF_MEMORY;
  52. FX_ENTRY ("DP::Init")
  53. WSADATA WSAData;
  54. int status;
  55. BOOL fDisableWS2;
  56. UINT uMinDelay;
  57. TCHAR *szKey = NACOBJECT_KEY TEXT("\\") RSVP_KEY;
  58. RegEntry reRSVP(szKey, HKEY_LOCAL_MACHINE, FALSE);
  59. if((NULL == hWnd) || (NULL == hInst))
  60. goto InitError;
  61. m_hAppInst = hInst;
  62. m_hAppWnd = hWnd;
  63. status = WSAStartup(MAKEWORD(1,1), &WSAData);
  64. if(status !=0)
  65. {
  66. ERRORMESSAGE(("CNac::Init:WSAStartup failed\r\n"));
  67. goto InitError;
  68. }
  69. // Introduce scope to allow creation of object after goto statements
  70. {
  71. // get settings from registry
  72. RegEntry reNac(szRegInternetPhone TEXT("\\") szRegInternetPhoneNac,
  73. HKEY_LOCAL_MACHINE,
  74. FALSE,
  75. KEY_READ);
  76. g_MaxAudioDelayMs = reNac.GetNumberIniStyle(TEXT ("MaxAudioDelayMs"), g_MaxAudioDelayMs);
  77. uMinDelay = reNac.GetNumberIniStyle(TEXT ("MinAudioDelayMs"), 0);
  78. if (uMinDelay != 0)
  79. {
  80. g_MinWaveAudioDelayMs = uMinDelay;
  81. g_MinDSEmulAudioDelayMs = uMinDelay;
  82. }
  83. fDisableWS2 = reNac.GetNumberIniStyle(TEXT ("DisableWinsock2"), 0);
  84. }
  85. #ifdef OLDSTUFF
  86. // to be safe, only try loading WS2_32 if WSOCK32 is passing
  87. // thru to it. Once we make sure that we link to the same DLL for all
  88. // Winsock calls to a socket, this check can possibly be removed.
  89. if (LOBYTE(WSAData.wHighVersion) >= 2 && !fDisableWS2)
  90. TryLoadWinsock2();
  91. #endif
  92. // Initialize data (should be in constructor)
  93. g_hEventHalfDuplex = CreateEvent (NULL, FALSE, TRUE, __TEXT ("AVC:HalfDuplex"));
  94. if (g_hEventHalfDuplex == NULL)
  95. {
  96. DEBUGMSG (ZONE_DP, ("%s: CreateEvent failed, LastErr=%lu\r\n", _fx_, GetLastError ()));
  97. hr = DPR_CANT_CREATE_EVENT;
  98. return hr;
  99. }
  100. // Initialize QoS. If it fails, that's Ok, we'll do without it.
  101. // No need to set the resource ourselves, this now done by the UI
  102. hr = CreateQoS (NULL, IID_IQoS, (void **)&m_pIQoS);
  103. if (hr != DPR_SUCCESS)
  104. m_pIQoS = (LPIQOS)NULL;
  105. m_bDisableRSVP = reRSVP.GetNumber("DisableRSVP", FALSE);
  106. LogInit(); // Initialize log
  107. //No receive channels yet
  108. m_nReceivers=0;
  109. // IVideoDevice initialize
  110. m_uVideoCaptureId = -1; // (VIDEO_MAPPER)
  111. // IAudioDevice initialize
  112. m_uWaveInID = WAVE_MAPPER;
  113. m_uWaveOutID = WAVE_MAPPER;
  114. m_bFullDuplex = FALSE;
  115. m_uSilenceLevel = 1000; // automatic silence detection
  116. m_bAutoMix = FALSE;
  117. m_bDirectSound = FALSE;
  118. return DPR_SUCCESS;
  119. InitError:
  120. ERRORMESSAGE( ("DataPump::Initialize: exit, hr=0x%lX\r\n", hr));
  121. return hr;
  122. }
  123. STDMETHODIMP
  124. DataPump::CreateMediaChannel( UINT flags, IMediaChannel **ppIMC)
  125. {
  126. IUnknown *pUnkOuter = NULL;
  127. IMediaChannel *pStream = NULL;
  128. HRESULT hr = E_FAIL;
  129. // try to be consistant about which parent classes we cast to
  130. *ppIMC = NULL;
  131. if (flags & MCF_AUDIO)
  132. {
  133. if ((flags & MCF_SEND) && !m_Audio.pSendStream)
  134. {
  135. if (m_bDirectSound && (DSC_Manager::Initialize() == S_OK))
  136. {
  137. DBG_SAVE_FILE_LINE
  138. pStream = (IMediaChannel*)(SendMediaStream*)new SendDSCStream;
  139. }
  140. else
  141. {
  142. DBG_SAVE_FILE_LINE
  143. pStream = (IMediaChannel*)(SendMediaStream*)new SendAudioStream;
  144. }
  145. }
  146. else if ((flags & MCF_RECV) && !m_Audio.pRecvStream)
  147. {
  148. if (m_bDirectSound && (DirectSoundMgr::Initialize() == S_OK))
  149. {
  150. DBG_SAVE_FILE_LINE
  151. pStream = (IMediaChannel*)(RecvMediaStream*)new RecvDSAudioStream;
  152. }
  153. else
  154. {
  155. DBG_SAVE_FILE_LINE
  156. pStream = (IMediaChannel*)(RecvMediaStream*)new RecvAudioStream;
  157. }
  158. }
  159. }
  160. else if (flags & MCF_VIDEO)
  161. {
  162. if ((flags & MCF_SEND) && !m_Video.pSendStream)
  163. {
  164. DBG_SAVE_FILE_LINE
  165. pStream = (IMediaChannel*)(SendMediaStream*) new SendVideoStream;
  166. }
  167. else if ((flags & MCF_RECV) && !m_Video.pRecvStream)
  168. {
  169. DBG_SAVE_FILE_LINE
  170. pStream = (IMediaChannel*)(RecvMediaStream*) new RecvVideoStream;
  171. }
  172. }
  173. else
  174. hr = E_INVALIDARG;
  175. if (pStream != NULL) {
  176. // need to inc the refCount of the object
  177. pStream->AddRef();
  178. hr = (flags & MCF_SEND) ?
  179. ((SendMediaStream *)pStream)->Initialize( this)
  180. : ((RecvMediaStream *)pStream)->Initialize(this);
  181. if (hr == S_OK)
  182. {
  183. hr = pStream->QueryInterface(IID_IMediaChannel, (void **)ppIMC);
  184. }
  185. if (hr == S_OK)
  186. {
  187. AddMediaChannel(flags, pStream);
  188. }
  189. // calling to the IVideoDevice and IAudioDevice methods
  190. // prior to creating the corresponding channel object
  191. // require when they get created
  192. // video only needs it's device ID set
  193. if ((flags & MCF_SEND) && (flags & MCF_VIDEO))
  194. {
  195. SetCurrCapDevID(m_uVideoCaptureId);
  196. }
  197. // audio streams need several properties set
  198. if (flags & MCF_AUDIO)
  199. {
  200. if (flags & MCF_SEND)
  201. {
  202. SetSilenceLevel(m_uSilenceLevel);
  203. SetAutoMix(m_bAutoMix);
  204. SetRecordID(m_uWaveInID);
  205. }
  206. else if (flags & MCF_RECV)
  207. {
  208. SetPlaybackID(m_uWaveOutID);
  209. }
  210. SetStreamDuplex(pStream, m_bFullDuplex);
  211. }
  212. // to avoid a circular ref-count,
  213. // dont keep a hard reference to MediaChannel objects
  214. // MediaChannel will call RemoveMediaChannel before it goes away..
  215. pStream->Release();
  216. pStream = NULL;
  217. }
  218. return hr;
  219. }
  220. STDMETHODIMP DataPump::SetStreamEventObj(IStreamEventNotify *pNotify)
  221. {
  222. EnterCriticalSection(&m_crs);
  223. if (m_pTEP)
  224. {
  225. delete m_pTEP;
  226. m_pTEP = NULL;
  227. }
  228. if (pNotify)
  229. {
  230. DBG_SAVE_FILE_LINE
  231. m_pTEP = new ThreadEventProxy(pNotify, m_hAppInst);
  232. }
  233. LeaveCriticalSection(&m_crs);
  234. return S_OK;
  235. }
  236. // this function gets called by the stream threads when an event occurs
  237. STDMETHODIMP DataPump::StreamEvent(UINT uDirection, UINT uMediaType,
  238. UINT uEventType, UINT uSubCode)
  239. {
  240. BOOL bRet = FALSE;
  241. EnterCriticalSection(&m_crs);
  242. if (m_pTEP)
  243. {
  244. bRet = m_pTEP->ThreadEvent(uDirection, uMediaType, uEventType, uSubCode);
  245. }
  246. LeaveCriticalSection(&m_crs);
  247. return bRet ? DPR_INVALID_PARAMETER : DPR_SUCCESS;
  248. }
  249. void
  250. DataPump::AddMediaChannel(UINT flags, IMediaChannel *pMediaChannel)
  251. {
  252. EnterCriticalSection(&m_crs);
  253. if (flags & MCF_SEND)
  254. {
  255. SendMediaStream *pS = static_cast<SendMediaStream *> (pMediaChannel);
  256. if (flags & MCF_AUDIO)
  257. m_Audio.pSendStream = pS;
  258. else if (flags & MCF_VIDEO)
  259. m_Video.pSendStream = pS;
  260. }
  261. else if (flags & MCF_RECV)
  262. {
  263. RecvMediaStream *pR = static_cast<RecvMediaStream *> (pMediaChannel);
  264. if (flags & MCF_AUDIO)
  265. m_Audio.pRecvStream = pR;
  266. else if (flags & MCF_VIDEO)
  267. m_Video.pRecvStream = pR;
  268. }
  269. LeaveCriticalSection(&m_crs);
  270. }
  271. void
  272. DataPump::RemoveMediaChannel(UINT flags, IMediaChannel *pMediaChannel)
  273. {
  274. EnterCriticalSection(&m_crs);
  275. if (flags & MCF_SEND)
  276. {
  277. if (flags & MCF_AUDIO)
  278. {
  279. ASSERT(pMediaChannel == m_Audio.pSendStream);
  280. if (pMediaChannel == m_Audio.pSendStream)
  281. m_Audio.pSendStream = NULL;
  282. }
  283. else if (flags & MCF_VIDEO)
  284. {
  285. ASSERT(pMediaChannel == m_Video.pSendStream);
  286. m_Video.pSendStream = NULL;
  287. }
  288. }
  289. else if (flags & MCF_RECV)
  290. {
  291. if (flags & MCF_AUDIO)
  292. {
  293. ASSERT(pMediaChannel == m_Audio.pRecvStream);
  294. m_Audio.pRecvStream = NULL;
  295. }
  296. else if (flags & MCF_VIDEO)
  297. {
  298. ASSERT(pMediaChannel == m_Video.pRecvStream);
  299. m_Video.pRecvStream = NULL;
  300. }
  301. }
  302. LeaveCriticalSection(&m_crs);
  303. }
  304. // called by Record Thread and Receive Thread, usually to get the
  305. // opposite channel
  306. HRESULT DataPump::GetMediaChannelInterface( UINT flags, IMediaChannel **ppI)
  307. {
  308. // extern IID IID_IMediaChannel;
  309. IMediaChannel *pStream = NULL;
  310. HRESULT hr;
  311. EnterCriticalSection(&m_crs);
  312. if (flags & MCF_AUDIO) {
  313. if (flags & MCF_SEND) {
  314. pStream = m_Audio.pSendStream;
  315. } else if (flags & MCF_RECV) {
  316. pStream = m_Audio.pRecvStream;
  317. }
  318. }
  319. else if (flags & MCF_VIDEO) {
  320. if (flags & MCF_SEND) {
  321. pStream = m_Video.pSendStream;
  322. } else if (flags & MCF_RECV) {
  323. pStream = m_Video.pRecvStream;
  324. }
  325. } else
  326. hr = DPR_INVALID_PARAMETER;
  327. if (pStream) {
  328. // need to inc the refCount of the object
  329. hr = (pStream)->QueryInterface(IID_IMediaChannel, (PVOID *)ppI);
  330. } else
  331. hr = E_NOINTERFACE;
  332. LeaveCriticalSection(&m_crs);
  333. return hr;
  334. }
  335. DWORD __stdcall StartDPRecvThread(PVOID pVoid)
  336. {
  337. DataPump *pDP = (DataPump*)pVoid;
  338. return pDP->CommonWS2RecvThread();
  339. }
  340. STDMETHODIMP DataPump::QueryInterface( REFIID iid, void ** ppvObject)
  341. {
  342. // this breaks the rules for the official COM QueryInterface because
  343. // the interfaces that are queried for are not necessarily real COM
  344. // interfaces. The reflexive property of QueryInterface would be broken in
  345. // that case.
  346. HRESULT hr = E_NOINTERFACE;
  347. if(!ppvObject)
  348. return hr;
  349. *ppvObject = NULL;
  350. if(iid == IID_IUnknown)// satisfy symmetric property of QI
  351. {
  352. *ppvObject = this;
  353. hr = hrSuccess;
  354. AddRef();
  355. }
  356. else if(iid == IID_IMediaChannelBuilder)
  357. {
  358. *ppvObject = (IMediaChannelBuilder *)this;
  359. hr = hrSuccess;
  360. AddRef();
  361. }
  362. else if (iid == IID_IVideoDevice)
  363. {
  364. *ppvObject = (IVideoDevice *)this;
  365. hr = hrSuccess;
  366. AddRef();
  367. }
  368. else if (iid == IID_IAudioDevice)
  369. {
  370. *ppvObject = (IAudioDevice*)this;
  371. hr = hrSuccess;
  372. AddRef();
  373. }
  374. return (hr);
  375. }
  376. ULONG DataPump::AddRef()
  377. {
  378. m_uRef++;
  379. return m_uRef;
  380. }
  381. ULONG DataPump::Release()
  382. {
  383. m_uRef--;
  384. if(m_uRef == 0)
  385. {
  386. m_hAppWnd = NULL;
  387. m_hAppInst = NULL;
  388. delete this;
  389. return 0;
  390. }
  391. return m_uRef;
  392. }
  393. void
  394. DataPump::ReleaseResources()
  395. {
  396. FX_ENTRY ("DP::ReleaseResources")
  397. #ifdef DEBUG
  398. if (m_Audio.pSendStream)
  399. ERRORMESSAGE(("%s: Audio Send stream still around => Ref count LEAK!\n", _fx_));
  400. if (m_Audio.pRecvStream)
  401. ERRORMESSAGE(("%s: Audio Recv stream still around => Ref count LEAK!\n", _fx_));
  402. if (m_Video.pSendStream)
  403. ERRORMESSAGE(("%s: Video Send stream still around => Ref count LEAK!\n", _fx_));
  404. if (m_Video.pRecvStream)
  405. ERRORMESSAGE(("%s: Video Recv stream still around => Ref count LEAK!\n", _fx_));
  406. #endif
  407. // close debug log
  408. LogClose();
  409. // Free QoS resources
  410. if (m_pIQoS)
  411. {
  412. m_pIQoS->Release();
  413. m_pIQoS = (LPIQOS)NULL;
  414. }
  415. // Close the receive and transmit streams
  416. if (g_hEventHalfDuplex)
  417. {
  418. CloseHandle (g_hEventHalfDuplex);
  419. g_hEventHalfDuplex = NULL;
  420. }
  421. }
  422. HRESULT DataPump::SetStreamDuplex(IMediaChannel *pStream, BOOL bFullDuplex)
  423. {
  424. BOOL fOn = (pStream->GetState() == MSSTATE_STARTED);
  425. BOOL bStreamFullDuplex;
  426. UINT uSize = sizeof(BOOL);
  427. pStream->GetProperty(PROP_DUPLEX_TYPE, &bStreamFullDuplex, &uSize);
  428. if (bStreamFullDuplex != bFullDuplex)
  429. {
  430. if (fOn)
  431. {
  432. pStream->Stop();
  433. }
  434. pStream->SetProperty(DP_PROP_DUPLEX_TYPE, &bFullDuplex, sizeof(BOOL));
  435. if (fOn)
  436. {
  437. pStream->Start();
  438. }
  439. }
  440. return S_OK;
  441. }
  442. HRESULT __stdcall DataPump::SetDuplex(BOOL bFullDuplex)
  443. {
  444. IMediaChannel *pS = m_Audio.pSendStream;
  445. IMediaChannel *pR = m_Audio.pRecvStream;
  446. IMediaChannel *pStream;
  447. BOOL fPlayOn = FALSE;
  448. BOOL fRecOn = FALSE;
  449. UINT uSize;
  450. BOOL bRecDuplex, bPlayDuplex;
  451. m_bFullDuplex = bFullDuplex ? TRUE : FALSE;
  452. UPDATE_REPORT_ENTRY(g_prptSystemSettings, (m_bFullDuplex) ? 1 : 0, REP_SYS_AUDIO_DUPLEX);
  453. RETAILMSG(("NAC: Audio Duplex Type: %s",(m_bFullDuplex) ? "Full Duplex" : "Half Duplex"));
  454. // no streams ? No problem.
  455. if ((pS == NULL) && (pR == NULL))
  456. {
  457. return S_OK;
  458. }
  459. // only one stream
  460. if ((pS || pR) && !(pS && pR))
  461. {
  462. if (pS)
  463. pStream = pS;
  464. else
  465. pStream = pR;
  466. return SetStreamDuplex(pStream, m_bFullDuplex);
  467. }
  468. // assert - pS && pR
  469. // both streams exist
  470. // try to avoid the whole start/stop sequence if the duplex
  471. // is the same
  472. uSize=sizeof(BOOL);
  473. pR->GetProperty(PROP_DUPLEX_TYPE, &bRecDuplex, &uSize);
  474. uSize=sizeof(BOOL);
  475. pS->GetProperty(PROP_DUPLEX_TYPE, &bPlayDuplex, &uSize);
  476. if ( (bPlayDuplex == m_bFullDuplex) &&
  477. (bRecDuplex == m_bFullDuplex))
  478. {
  479. return S_OK;
  480. }
  481. // save the old thread flags
  482. fPlayOn = (pR->GetState() == MSSTATE_STARTED);
  483. fRecOn = (pS->GetState() == MSSTATE_STARTED);
  484. // Ensure the record and playback threads are stopped
  485. pR->Stop();
  486. pS->Stop();
  487. SetStreamDuplex(pR, m_bFullDuplex);
  488. SetStreamDuplex(pS, m_bFullDuplex);
  489. // Resume the record/playback
  490. // Try to let play start before record - DirectS and SB16 prefer that!
  491. if (fPlayOn)
  492. {
  493. pR->Start();
  494. }
  495. if (fRecOn)
  496. {
  497. pS->Start();
  498. }
  499. return DPR_SUCCESS;
  500. }
  501. #define LONGTIME 60000 // 60 seconds
  502. // utility function to synchronously communicate a
  503. // a state change to the recv thread
  504. HRESULT DataPump::RecvThreadMessage(UINT msg, RecvMediaStream *pMS)
  505. {
  506. BOOL fSignaled;
  507. DWORD dwWaitStatus;
  508. HANDLE handle;
  509. // Unfortunately cant use PostThreadMessage to signal the thread
  510. // because it doesnt have a message loop
  511. m_pCurRecvStream = pMS;
  512. m_CurRecvMsg = msg;
  513. fSignaled = SetEvent(m_hRecvThreadSignalEvent);
  514. if (fSignaled) {
  515. handle = (msg == MSG_EXIT_RECV ? m_hRecvThread : m_hRecvThreadAckEvent);
  516. dwWaitStatus = WaitForSingleObject(handle, LONGTIME);
  517. ASSERT(dwWaitStatus == WAIT_OBJECT_0);
  518. if (dwWaitStatus != WAIT_OBJECT_0)
  519. return GetLastError();
  520. } else
  521. return GetLastError();
  522. return S_OK;
  523. }
  524. // start receiving on this stream
  525. // will create the receive thread if necessary.
  526. HRESULT
  527. DataPump::StartReceiving(RecvMediaStream *pMS)
  528. {
  529. DWORD dwWaitStatus;
  530. FX_ENTRY("DP::StartReceiving")
  531. // one more stream
  532. m_nReceivers++;
  533. if (!m_hRecvThread) {
  534. ASSERT(m_nReceivers==1);
  535. ASSERT(!m_hRecvThreadAckEvent);
  536. //Use this for thread event notifications. I.e. Video started/stopped, audio stopped, et al.
  537. //m_hRecvThreadChangeEvent=CreateEvent (NULL,FALSE,FALSE,NULL);
  538. //create the stopping sync event
  539. m_hRecvThreadAckEvent=CreateEvent (NULL,FALSE,FALSE,NULL);
  540. m_hRecvThreadSignalEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  541. m_hRecvThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)StartDPRecvThread,(PVOID)this,0,&m_RecvThId);
  542. DEBUGMSG(ZONE_DP,("%s: RecvThread Id=%x\n",_fx_,m_RecvThId));
  543. // thread will signal event soon as its message loop is ready
  544. dwWaitStatus = WaitForSingleObject(m_hRecvThreadAckEvent, LONGTIME);
  545. ASSERT(dwWaitStatus == WAIT_OBJECT_0);
  546. }
  547. //Tell the recv Thread to start receiving on this MediaStream
  548. return RecvThreadMessage(MSG_START_RECV,pMS);
  549. }
  550. // Stop receiving on the stream
  551. // will stop the receive thread if necessary
  552. HRESULT
  553. DataPump::StopReceiving(RecvMediaStream *pMS)
  554. {
  555. HANDLE rgh[2];
  556. ASSERT(m_nReceivers > 0);
  557. ASSERT(m_hRecvThread);
  558. if (m_nReceivers > 0)
  559. m_nReceivers--;
  560. RecvThreadMessage(MSG_STOP_RECV, pMS);
  561. if (!m_nReceivers && m_hRecvThread) {
  562. // kill the receive thread
  563. RecvThreadMessage(MSG_EXIT_RECV,NULL);
  564. CloseHandle(m_hRecvThread);
  565. CloseHandle(m_hRecvThreadAckEvent);
  566. m_hRecvThread = NULL;
  567. m_hRecvThreadAckEvent = NULL;
  568. if (m_hRecvThreadSignalEvent) {
  569. CloseHandle(m_hRecvThreadSignalEvent);
  570. m_hRecvThreadSignalEvent = NULL;
  571. }
  572. }
  573. return S_OK;
  574. }
  575. //
  576. // IVideoDevice Methods
  577. //
  578. // Capture device methods
  579. // Gets the number of enabled capture devices
  580. // Returns -1L on error
  581. HRESULT __stdcall DataPump::GetNumCapDev()
  582. {
  583. FINDCAPTUREDEVICE fcd;
  584. // scan for broken or unplugged devices
  585. FindFirstCaptureDevice(&fcd, NULL);
  586. return (GetNumCaptureDevices());
  587. }
  588. // Gets the max size of the captuire device name
  589. // Returns -1L on error
  590. HRESULT __stdcall DataPump::GetMaxCapDevNameLen()
  591. {
  592. return (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION);
  593. }
  594. // Enum list of enabled capture devices
  595. // Fills up 1st buffer with device IDs, 2nd buffer with device names
  596. // Third parameter is the max number of devices to enum
  597. // Returns number of devices enum-ed
  598. HRESULT __stdcall DataPump::EnumCapDev(DWORD *pdwCapDevIDs, TCHAR *pszCapDevNames, DWORD dwNumCapDev)
  599. {
  600. FINDCAPTUREDEVICE fcd;
  601. DWORD dwNumCapDevFound = 0;
  602. fcd.dwSize = sizeof (FINDCAPTUREDEVICE);
  603. if (FindFirstCaptureDevice(&fcd, NULL))
  604. {
  605. do
  606. {
  607. pdwCapDevIDs[dwNumCapDevFound] = fcd.nDeviceIndex;
  608. // Build device name out of the capture device strings
  609. if (fcd.szDeviceDescription && fcd.szDeviceDescription[0] != '\0')
  610. lstrcpy(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), fcd.szDeviceDescription);
  611. else
  612. lstrcpy(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), fcd.szDeviceName);
  613. if (fcd.szDeviceVersion && fcd.szDeviceVersion[0] != '\0')
  614. {
  615. lstrcat(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), ", ");
  616. lstrcat(pszCapDevNames + dwNumCapDevFound * (MAX_CAPDEV_NAME + MAX_CAPDEV_DESCRIPTION), fcd.szDeviceVersion);
  617. }
  618. dwNumCapDevFound++;
  619. } while ((dwNumCapDevFound < dwNumCapDev) && FindNextCaptureDevice(&fcd));
  620. }
  621. return (dwNumCapDevFound);
  622. }
  623. HRESULT __stdcall DataPump::GetCurrCapDevID()
  624. {
  625. UINT uCapID;
  626. UINT uSize = sizeof(UINT);
  627. // even though we know the value of the last call
  628. // to SetCurrCapDevID, the stream may have resulted in using
  629. // wave_mapper (-1). We want to be able to return -1, if this
  630. // is the case. However, the channel objects don't do this yet.
  631. // (they still return the same value as m_uVideoCaptureId)
  632. if (m_Video.pSendStream)
  633. {
  634. m_Video.pSendStream->GetProperty(PROP_CAPTURE_DEVICE, &uCapID, &uSize);
  635. #ifdef DEBUG
  636. if (uCapID != m_uVideoCaptureId)
  637. {
  638. DEBUGMSG(ZONE_DP,("Video capture stream had to revert to MAPPER or some other device"));
  639. }
  640. #endif
  641. return uCapID;
  642. }
  643. return m_uVideoCaptureId;
  644. }
  645. HRESULT __stdcall DataPump::SetCurrCapDevID(int nCapDevID)
  646. {
  647. m_uVideoCaptureId = (UINT)nCapDevID;
  648. if (m_Video.pSendStream)
  649. {
  650. m_Video.pSendStream->SetProperty(PROP_CAPTURE_DEVICE, &m_uVideoCaptureId, sizeof(m_uVideoCaptureId));
  651. }
  652. return S_OK;
  653. }
  654. // IAudioDevice methods
  655. HRESULT __stdcall DataPump::GetRecordID(UINT *puWaveDevID)
  656. {
  657. *puWaveDevID = m_uWaveInID;
  658. return S_OK;
  659. }
  660. HRESULT __stdcall DataPump::SetRecordID(UINT uWaveDevID)
  661. {
  662. m_uWaveInID = uWaveDevID;
  663. if (m_Audio.pSendStream)
  664. {
  665. m_Audio.pSendStream->SetProperty(PROP_RECORD_DEVICE, &m_uWaveInID, sizeof(m_uWaveInID));
  666. }
  667. return S_OK;
  668. }
  669. HRESULT __stdcall DataPump::GetPlaybackID(UINT *puWaveDevID)
  670. {
  671. // like video, the audio device may have resorted to using
  672. // WAVE_MAPPER. We'd like to be able to detect that
  673. *puWaveDevID = m_uWaveOutID;
  674. return S_OK;
  675. }
  676. HRESULT __stdcall DataPump::SetPlaybackID(UINT uWaveDevID)
  677. {
  678. m_uWaveOutID = uWaveDevID;
  679. if (m_Audio.pRecvStream)
  680. {
  681. m_Audio.pRecvStream->SetProperty(PROP_PLAYBACK_DEVICE, &m_uWaveOutID, sizeof(m_uWaveOutID));
  682. }
  683. return S_OK;
  684. }
  685. HRESULT __stdcall DataPump::GetSilenceLevel(UINT *puLevel)
  686. {
  687. *puLevel = m_uSilenceLevel;
  688. return S_OK;
  689. }
  690. HRESULT __stdcall DataPump::SetSilenceLevel(UINT uLevel)
  691. {
  692. m_uSilenceLevel = uLevel;
  693. if (m_Audio.pSendStream)
  694. {
  695. m_Audio.pSendStream->SetProperty(PROP_SILENCE_LEVEL, &m_uSilenceLevel, sizeof(m_uSilenceLevel));
  696. }
  697. return S_OK;
  698. }
  699. HRESULT __stdcall DataPump::GetDuplex(BOOL *pbFullDuplex)
  700. {
  701. *pbFullDuplex = m_bFullDuplex;
  702. return S_OK;
  703. }
  704. HRESULT __stdcall DataPump::GetMixer(HWND hwnd, BOOL bPlayback, IMixer **ppMixer)
  705. {
  706. CMixerDevice *pMixerDevice = NULL;
  707. DWORD dwFlags;
  708. HRESULT hr = E_NOINTERFACE;
  709. // unfortunately, trying to create a mixer when WAVE_MAPPER
  710. // has been specified as the device ID results in a mixer
  711. // that doesn't work on Win95.
  712. *ppMixer = NULL;
  713. if ((bPlayback) && (m_uWaveOutID != WAVE_MAPPER))
  714. {
  715. pMixerDevice = CMixerDevice::GetMixerForWaveDevice(hwnd, m_uWaveOutID, MIXER_OBJECTF_WAVEOUT);
  716. }
  717. else if (m_uWaveInID != WAVE_MAPPER)
  718. {
  719. pMixerDevice = CMixerDevice::GetMixerForWaveDevice(hwnd, m_uWaveInID, MIXER_OBJECTF_WAVEIN);
  720. }
  721. if (pMixerDevice)
  722. {
  723. hr = pMixerDevice->QueryInterface(IID_IMixer, (void**)ppMixer);
  724. }
  725. return hr;
  726. }
  727. HRESULT __stdcall DataPump::GetAutoMix(BOOL *pbAutoMix)
  728. {
  729. *pbAutoMix = m_bAutoMix;
  730. return S_OK;
  731. }
  732. HRESULT __stdcall DataPump::SetAutoMix(BOOL bAutoMix)
  733. {
  734. m_bAutoMix = bAutoMix;
  735. if (m_Audio.pSendStream)
  736. {
  737. m_Audio.pSendStream->SetProperty(PROP_AUDIO_AUTOMIX, &m_bAutoMix, sizeof(m_bAutoMix));
  738. }
  739. return S_OK;
  740. }
  741. HRESULT __stdcall DataPump::GetDirectSound(BOOL *pbDS)
  742. {
  743. *pbDS = m_bDirectSound;
  744. return S_OK;
  745. }
  746. HRESULT __stdcall DataPump::SetDirectSound(BOOL bDS)
  747. {
  748. m_bDirectSound = bDS;
  749. return S_OK;
  750. }