FreeRDP
wf_directsound.c
1 #include "wf_directsound.h"
2 #include "wf_interface.h"
3 #include "wf_info.h"
4 #include "wf_rdpsnd.h"
5 
6 #include <winpr/windows.h>
7 
8 #define INITGUID
9 #include <initguid.h>
10 #include <objbase.h>
11 
12 #define CINTERFACE 1
13 #include <mmsystem.h>
14 #include <dsound.h>
15 
16 #include <freerdp/log.h>
17 #define TAG SERVER_TAG("windows")
18 
19 IDirectSoundCapture8* cap;
20 IDirectSoundCaptureBuffer8* capBuf;
21 DSCBUFFERDESC dscbd;
22 DWORD lastPos;
23 wfPeerContext* latestPeer;
24 
25 int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
26 {
27  latestPeer = peer;
28  return 0;
29 }
30 
31 int wf_directsound_activate(RdpsndServerContext* context)
32 {
33  HRESULT hr;
34  wfInfo* wfi;
35  HANDLE hThread;
36 
37  LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
38 
39  wfi = wf_info_get_instance();
40  if (!wfi)
41  {
42  WLog_ERR(TAG, "Failed to wfi instance");
43  return 1;
44  }
45  WLog_DBG(TAG, "RDPSND (direct sound) Activated");
46  hr = DirectSoundCaptureCreate8(NULL, &cap, NULL);
47 
48  if (FAILED(hr))
49  {
50  WLog_ERR(TAG, "Failed to create sound capture device");
51  return 1;
52  }
53 
54  WLog_INFO(TAG, "Created sound capture device");
55  dscbd.dwSize = sizeof(DSCBUFFERDESC);
56  dscbd.dwFlags = 0;
57  dscbd.dwBufferBytes = wfi->agreed_format->nAvgBytesPerSec;
58  dscbd.dwReserved = 0;
59  dscbd.lpwfxFormat = wfi->agreed_format;
60  dscbd.dwFXCount = 0;
61  dscbd.lpDSCFXDesc = NULL;
62 
63  hr = cap->lpVtbl->CreateCaptureBuffer(cap, &dscbd, &pDSCB, NULL);
64 
65  if (FAILED(hr))
66  {
67  WLog_ERR(TAG, "Failed to create capture buffer");
68  }
69 
70  WLog_INFO(TAG, "Created capture buffer");
71  hr = pDSCB->lpVtbl->QueryInterface(pDSCB, &IID_IDirectSoundCaptureBuffer8, (LPVOID*)&capBuf);
72  if (FAILED(hr))
73  {
74  WLog_ERR(TAG, "Failed to QI capture buffer");
75  }
76  WLog_INFO(TAG, "Created IDirectSoundCaptureBuffer8");
77  pDSCB->lpVtbl->Release(pDSCB);
78  lastPos = 0;
79 
80  if (!(hThread = CreateThread(NULL, 0, wf_rdpsnd_directsound_thread, latestPeer, 0, NULL)))
81  {
82  WLog_ERR(TAG, "Failed to create direct sound thread");
83  return 1;
84  }
85  (void)CloseHandle(hThread);
86 
87  return 0;
88 }
89 
90 static DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam)
91 {
92  HRESULT hr;
93  DWORD beg = 0;
94  DWORD end = 0;
95  DWORD diff, rate;
96  wfPeerContext* context;
97  wfInfo* wfi;
98 
99  VOID* pbCaptureData = NULL;
100  DWORD dwCaptureLength = 0;
101  VOID* pbCaptureData2 = NULL;
102  DWORD dwCaptureLength2 = 0;
103  VOID* pbPlayData = NULL;
104  DWORD dwReadPos = 0;
105  LONG lLockSize = 0;
106 
107  wfi = wf_info_get_instance();
108  if (!wfi)
109  {
110  WLog_ERR(TAG, "Failed get instance");
111  return 1;
112  }
113 
114  context = (wfPeerContext*)lpParam;
115  rate = 1000 / 24;
116  WLog_INFO(TAG, "Trying to start capture");
117  hr = capBuf->lpVtbl->Start(capBuf, DSCBSTART_LOOPING);
118  if (FAILED(hr))
119  {
120  WLog_ERR(TAG, "Failed to start capture");
121  }
122  WLog_INFO(TAG, "Capture started");
123 
124  while (1)
125  {
126 
127  end = GetTickCount();
128  diff = end - beg;
129 
130  if (diff < rate)
131  {
132  Sleep(rate - diff);
133  }
134 
135  beg = GetTickCount();
136 
137  if (wf_rdpsnd_lock() > 0)
138  {
139  // check for main exit condition
140  if (wfi->snd_stop == TRUE)
141  {
142  wf_rdpsnd_unlock();
143  break;
144  }
145 
146  hr = capBuf->lpVtbl->GetCurrentPosition(capBuf, NULL, &dwReadPos);
147  if (FAILED(hr))
148  {
149  WLog_ERR(TAG, "Failed to get read pos");
150  wf_rdpsnd_unlock();
151  break;
152  }
153 
154  lLockSize = dwReadPos - lastPos; // dscbd.dwBufferBytes;
155  if (lLockSize < 0)
156  lLockSize += dscbd.dwBufferBytes;
157 
158  // WLog_DBG(TAG, "Last, read, lock = [%"PRIu32", %"PRIu32", %"PRId32"]\n", lastPos,
159  // dwReadPos, lLockSize);
160 
161  if (lLockSize == 0)
162  {
163  wf_rdpsnd_unlock();
164  continue;
165  }
166 
167  hr = capBuf->lpVtbl->Lock(capBuf, lastPos, lLockSize, &pbCaptureData, &dwCaptureLength,
168  &pbCaptureData2, &dwCaptureLength2, 0L);
169  if (FAILED(hr))
170  {
171  WLog_ERR(TAG, "Failed to lock sound capture buffer");
172  wf_rdpsnd_unlock();
173  break;
174  }
175 
176  // fwrite(pbCaptureData, 1, dwCaptureLength, pFile);
177  // fwrite(pbCaptureData2, 1, dwCaptureLength2, pFile);
178 
179  // FIXME: frames = bytes/(bytespersample * channels)
180 
181  context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData, dwCaptureLength / 4,
182  (UINT16)(beg & 0xffff));
183  context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData2, dwCaptureLength2 / 4,
184  (UINT16)(beg & 0xffff));
185 
186  hr = capBuf->lpVtbl->Unlock(capBuf, pbCaptureData, dwCaptureLength, pbCaptureData2,
187  dwCaptureLength2);
188  if (FAILED(hr))
189  {
190  WLog_ERR(TAG, "Failed to unlock sound capture buffer");
191  wf_rdpsnd_unlock();
192  return 0;
193  }
194 
195  // TODO keep track of location in buffer
196  lastPos += dwCaptureLength;
197  lastPos %= dscbd.dwBufferBytes;
198  lastPos += dwCaptureLength2;
199  lastPos %= dscbd.dwBufferBytes;
200 
201  wf_rdpsnd_unlock();
202  }
203  }
204 
205  WLog_INFO(TAG, "Trying to stop sound capture");
206  hr = capBuf->lpVtbl->Stop(capBuf);
207  if (FAILED(hr))
208  {
209  WLog_ERR(TAG, "Failed to stop capture");
210  }
211 
212  WLog_INFO(TAG, "Capture stopped");
213  capBuf->lpVtbl->Release(capBuf);
214  cap->lpVtbl->Release(cap);
215 
216  lastPos = 0;
217 
218  return 0;
219 }