FreeRDP
Loading...
Searching...
No Matches
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
42typedef 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
60static 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
69static 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
78static 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
93static BOOL rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, UINT32 volume);
94
95static 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
109static 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
133static 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
154static 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
166static 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
176static 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
204static 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
227static 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
254static 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
281static 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
288static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device, const ADDIN_ARGV* args)
289{
290 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
291 COMMAND_LINE_ARGUMENT_A rdpsnd_opensles_args[] = {
292 { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
293 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
294 };
295
296 WINPR_ASSERT(opensles);
297 WINPR_ASSERT(args);
298 DEBUG_SND("opensles=%p, args=%p", (void*)opensles, (void*)args);
299 const DWORD flags =
300 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
301 const int status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_opensles_args,
302 flags, opensles, NULL, NULL);
303
304 if (status < 0)
305 return status;
306
307 const COMMAND_LINE_ARGUMENT_A* arg = rdpsnd_opensles_args;
308
309 do
310 {
311 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
312 continue;
313
314 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
315 {
316 opensles->device_name = _strdup(arg->Value);
317
318 if (!opensles->device_name)
319 return ERROR_OUTOFMEMORY;
320 }
321 CommandLineSwitchEnd(arg)
322 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
323
324 return status;
325}
326
332FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_rdpsnd_client_subsystem_entry(
334{
335 UINT error = ERROR_INTERNAL_ERROR;
336 DEBUG_SND("pEntryPoints=%p", (void*)pEntryPoints);
337 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)calloc(1, sizeof(rdpsndopenslesPlugin));
338
339 if (!opensles)
340 return CHANNEL_RC_NO_MEMORY;
341
342 opensles->device.Open = rdpsnd_opensles_open;
343 opensles->device.FormatSupported = rdpsnd_opensles_format_supported;
344 opensles->device.GetVolume = rdpsnd_opensles_get_volume;
345 opensles->device.SetVolume = rdpsnd_opensles_set_volume;
346 opensles->device.Start = rdpsnd_opensles_start;
347 opensles->device.Play = rdpsnd_opensles_play;
348 opensles->device.Close = rdpsnd_opensles_close;
349 opensles->device.Free = rdpsnd_opensles_free;
350 const ADDIN_ARGV* args = pEntryPoints->args;
351 rdpsnd_opensles_parse_addin_args((rdpsndDevicePlugin*)opensles, args);
352
353 if (!opensles->device_name)
354 {
355 opensles->device_name = _strdup("default");
356
357 if (!opensles->device_name)
358 {
359 error = CHANNEL_RC_NO_MEMORY;
360 goto outstrdup;
361 }
362 }
363
364 opensles->rate = 44100;
365 opensles->channels = 2;
366 opensles->format = WAVE_FORMAT_ADPCM;
367 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)opensles);
368 DEBUG_SND("success");
369 return CHANNEL_RC_OK;
370outstrdup:
371 free(opensles);
372 return error;
373}