FreeRDP
rdpsnd_opensles.c
1 
22 #include <freerdp/config.h>
23 
24 #include <winpr/assert.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdbool.h>
30 
31 #include <winpr/crt.h>
32 #include <winpr/cmdline.h>
33 #include <winpr/sysinfo.h>
34 #include <winpr/collections.h>
35 
36 #include <freerdp/types.h>
37 #include <freerdp/channels/log.h>
38 
39 #include "opensl_io.h"
40 #include "rdpsnd_main.h"
41 
42 typedef struct
43 {
44  rdpsndDevicePlugin device;
45 
46  UINT32 latency;
47  int wformat;
48  int block_size;
49  char* device_name;
50 
51  OPENSL_STREAM* stream;
52 
53  UINT32 volume;
54 
55  UINT32 rate;
56  UINT32 channels;
57  int format;
58 } rdpsndopenslesPlugin;
59 
60 static int rdpsnd_opensles_volume_to_millibel(unsigned short level, int max)
61 {
62  const int min = SL_MILLIBEL_MIN;
63  const int step = max - min;
64  const int rc = (level * step / 0xFFFF) + min;
65  DEBUG_SND("level=%hu, min=%d, max=%d, step=%d, result=%d", level, min, max, step, rc);
66  return rc;
67 }
68 
69 static unsigned short rdpsnd_opensles_millibel_to_volume(int millibel, int max)
70 {
71  const int min = SL_MILLIBEL_MIN;
72  const int range = max - min;
73  const int rc = ((millibel - min) * 0xFFFF + range / 2 + 1) / range;
74  DEBUG_SND("millibel=%d, min=%d, max=%d, range=%d, result=%d", millibel, min, max, range, rc);
75  return rc;
76 }
77 
78 static bool rdpsnd_opensles_check_handle(const rdpsndopenslesPlugin* hdl)
79 {
80  bool rc = true;
81 
82  if (!hdl)
83  rc = false;
84  else
85  {
86  if (!hdl->stream)
87  rc = false;
88  }
89 
90  return rc;
91 }
92 
93 static BOOL rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, UINT32 volume);
94 
95 static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles)
96 {
97  DEBUG_SND("opensles=%p", (void*)opensles);
98 
99  if (!rdpsnd_opensles_check_handle(opensles))
100  return 0;
101 
102  if (opensles->stream)
103  android_CloseAudioDevice(opensles->stream);
104 
105  opensles->stream = android_OpenAudioDevice(opensles->rate, opensles->channels, 20);
106  return 0;
107 }
108 
109 static BOOL rdpsnd_opensles_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
110  UINT32 latency)
111 {
112  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
113  rdpsnd_opensles_check_handle(opensles);
114  DEBUG_SND("opensles=%p format=%p, latency=%" PRIu32, (void*)opensles, (void*)format, latency);
115 
116  if (format)
117  {
118  DEBUG_SND("format=%" PRIu16 ", cbsize=%" PRIu16 ", samples=%" PRIu32 ", bits=%" PRIu16
119  ", channels=%" PRIu16 ", align=%" PRIu16 "",
120  format->wFormatTag, format->cbSize, format->nSamplesPerSec,
121  format->wBitsPerSample, format->nChannels, format->nBlockAlign);
122  opensles->rate = format->nSamplesPerSec;
123  opensles->channels = format->nChannels;
124  opensles->format = format->wFormatTag;
125  opensles->wformat = format->wFormatTag;
126  opensles->block_size = format->nBlockAlign;
127  }
128 
129  opensles->latency = latency;
130  return (rdpsnd_opensles_set_params(opensles) == 0);
131 }
132 
133 static BOOL rdpsnd_opensles_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
134  UINT32 latency)
135 {
136  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
137  DEBUG_SND("opensles=%p format=%p, latency=%" PRIu32 ", rate=%" PRIu32 "", (void*)opensles,
138  (void*)format, latency, opensles->rate);
139 
140  if (rdpsnd_opensles_check_handle(opensles))
141  return TRUE;
142 
143  opensles->stream = android_OpenAudioDevice(opensles->rate, opensles->channels, 20);
144  WINPR_ASSERT(opensles->stream);
145 
146  if (!opensles->stream)
147  WLog_ERR(TAG, "android_OpenAudioDevice failed");
148  else
149  rdpsnd_opensles_set_volume(device, opensles->volume);
150 
151  return rdpsnd_opensles_set_format(device, format, latency);
152 }
153 
154 static void rdpsnd_opensles_close(rdpsndDevicePlugin* device)
155 {
156  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
157  DEBUG_SND("opensles=%p", (void*)opensles);
158 
159  if (!rdpsnd_opensles_check_handle(opensles))
160  return;
161 
162  android_CloseAudioDevice(opensles->stream);
163  opensles->stream = NULL;
164 }
165 
166 static void rdpsnd_opensles_free(rdpsndDevicePlugin* device)
167 {
168  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
169  DEBUG_SND("opensles=%p", (void*)opensles);
170  WINPR_ASSERT(opensles);
171  WINPR_ASSERT(opensles->device_name);
172  free(opensles->device_name);
173  free(opensles);
174 }
175 
176 static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
177 {
178  DEBUG_SND("format=%" PRIu16 ", cbsize=%" PRIu16 ", samples=%" PRIu32 ", bits=%" PRIu16
179  ", channels=%" PRIu16 ", align=%" PRIu16 "",
180  format->wFormatTag, format->cbSize, format->nSamplesPerSec, format->wBitsPerSample,
181  format->nChannels, format->nBlockAlign);
182  WINPR_ASSERT(device);
183  WINPR_ASSERT(format);
184 
185  switch (format->wFormatTag)
186  {
187  case WAVE_FORMAT_PCM:
188  if (format->cbSize == 0 && format->nSamplesPerSec <= 48000 &&
189  (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
190  (format->nChannels == 1 || format->nChannels == 2))
191  {
192  return TRUE;
193  }
194 
195  break;
196 
197  default:
198  break;
199  }
200 
201  return FALSE;
202 }
203 
204 static UINT32 rdpsnd_opensles_get_volume(rdpsndDevicePlugin* device)
205 {
206  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
207  DEBUG_SND("opensles=%p", (void*)opensles);
208  WINPR_ASSERT(opensles);
209 
210  if (opensles->stream)
211  {
212  const int max = android_GetOutputVolumeMax(opensles->stream);
213  const int rc = android_GetOutputVolume(opensles->stream);
214 
215  if (android_GetOutputMute(opensles->stream))
216  opensles->volume = 0;
217  else
218  {
219  const unsigned short vol = rdpsnd_opensles_millibel_to_volume(rc, max);
220  opensles->volume = (vol << 16) | (vol & 0xFFFF);
221  }
222  }
223 
224  return opensles->volume;
225 }
226 
227 static BOOL rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, UINT32 value)
228 {
229  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
230  DEBUG_SND("opensles=%p, value=%" PRIu32 "", (void*)opensles, value);
231  WINPR_ASSERT(opensles);
232  opensles->volume = value;
233 
234  if (opensles->stream)
235  {
236  if (0 == opensles->volume)
237  return android_SetOutputMute(opensles->stream, true);
238  else
239  {
240  const int max = android_GetOutputVolumeMax(opensles->stream);
241  const int vol = rdpsnd_opensles_volume_to_millibel(value & 0xFFFF, max);
242 
243  if (!android_SetOutputMute(opensles->stream, false))
244  return FALSE;
245 
246  if (!android_SetOutputVolume(opensles->stream, vol))
247  return FALSE;
248  }
249  }
250 
251  return TRUE;
252 }
253 
254 static UINT rdpsnd_opensles_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
255 {
256  union
257  {
258  const BYTE* b;
259  const short* s;
260  } src;
261  int ret;
262  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
263  DEBUG_SND("opensles=%p, data=%p, size=%d", (void*)opensles, (void*)data, size);
264 
265  if (!rdpsnd_opensles_check_handle(opensles))
266  return 0;
267 
268  src.b = data;
269  DEBUG_SND("size=%d, src=%p", size, (void*)src.b);
270  WINPR_ASSERT(0 == size % 2);
271  WINPR_ASSERT(size > 0);
272  WINPR_ASSERT(src.b);
273  ret = android_AudioOut(opensles->stream, src.s, size / 2);
274 
275  if (ret < 0)
276  WLog_ERR(TAG, "android_AudioOut failed (%d)", ret);
277 
278  return 10; /* TODO: Get real latencry in [ms] */
279 }
280 
281 static void rdpsnd_opensles_start(rdpsndDevicePlugin* device)
282 {
283  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
284  rdpsnd_opensles_check_handle(opensles);
285  DEBUG_SND("opensles=%p", (void*)opensles);
286 }
287 
288 static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
289 {
290  int status;
291  DWORD flags;
292  const COMMAND_LINE_ARGUMENT_A* arg;
293  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
294  COMMAND_LINE_ARGUMENT_A rdpsnd_opensles_args[] = {
295  { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
296  { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
297  };
298 
299  WINPR_ASSERT(opensles);
300  WINPR_ASSERT(args);
301  DEBUG_SND("opensles=%p, args=%p", (void*)opensles, (void*)args);
302  flags =
303  COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
304  status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_opensles_args, flags,
305  opensles, NULL, NULL);
306 
307  if (status < 0)
308  return status;
309 
310  arg = rdpsnd_opensles_args;
311 
312  do
313  {
314  if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
315  continue;
316 
317  CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
318  {
319  opensles->device_name = _strdup(arg->Value);
320 
321  if (!opensles->device_name)
322  return ERROR_OUTOFMEMORY;
323  }
324  CommandLineSwitchEnd(arg)
325  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
326 
327  return status;
328 }
329 
335 FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_rdpsnd_client_subsystem_entry(
337 {
338  ADDIN_ARGV* args;
339  rdpsndopenslesPlugin* opensles;
340  UINT error;
341  DEBUG_SND("pEntryPoints=%p", (void*)pEntryPoints);
342  opensles = (rdpsndopenslesPlugin*)calloc(1, sizeof(rdpsndopenslesPlugin));
343 
344  if (!opensles)
345  return CHANNEL_RC_NO_MEMORY;
346 
347  opensles->device.Open = rdpsnd_opensles_open;
348  opensles->device.FormatSupported = rdpsnd_opensles_format_supported;
349  opensles->device.GetVolume = rdpsnd_opensles_get_volume;
350  opensles->device.SetVolume = rdpsnd_opensles_set_volume;
351  opensles->device.Start = rdpsnd_opensles_start;
352  opensles->device.Play = rdpsnd_opensles_play;
353  opensles->device.Close = rdpsnd_opensles_close;
354  opensles->device.Free = rdpsnd_opensles_free;
355  args = pEntryPoints->args;
356  rdpsnd_opensles_parse_addin_args((rdpsndDevicePlugin*)opensles, args);
357 
358  if (!opensles->device_name)
359  {
360  opensles->device_name = _strdup("default");
361 
362  if (!opensles->device_name)
363  {
364  error = CHANNEL_RC_NO_MEMORY;
365  goto outstrdup;
366  }
367  }
368 
369  opensles->rate = 44100;
370  opensles->channels = 2;
371  opensles->format = WAVE_FORMAT_ADPCM;
372  pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)opensles);
373  DEBUG_SND("success");
374  return CHANNEL_RC_OK;
375 outstrdup:
376  free(opensles);
377  return error;
378 }
Definition: client/rdpsnd.h:76