FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
audin_opensl_es.c
1
22#include <freerdp/config.h>
23
24#include <winpr/assert.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <winpr/crt.h>
30#include <winpr/cmdline.h>
31
32#include <freerdp/freerdp.h>
33#include <freerdp/addin.h>
34#include <freerdp/channels/rdpsnd.h>
35
36#include <SLES/OpenSLES.h>
37#include <freerdp/client/audin.h>
38
39#include "audin_main.h"
40#include "opensl_io.h"
41
42typedef struct
43{
44 IAudinDevice iface;
45
46 char* device_name;
47 OPENSL_STREAM* stream;
48
49 AUDIO_FORMAT format;
50 UINT32 frames_per_packet;
51
52 UINT32 bytes_per_channel;
53
54 AudinReceive receive;
55
56 void* user_data;
57
58 rdpContext* rdpcontext;
59 wLog* log;
60} AudinOpenSLESDevice;
61
62static UINT audin_opensles_close(IAudinDevice* device);
63
64static void audin_receive(void* context, const void* data, size_t size)
65{
66 UINT error;
67 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)context;
68
69 if (!opensles || !data)
70 {
71 WLog_ERR(TAG, "Invalid arguments context=%p, data=%p", opensles, data);
72 return;
73 }
74
75 error = opensles->receive(&opensles->format, data, size, opensles->user_data);
76
77 if (error && opensles->rdpcontext)
78 setChannelError(opensles->rdpcontext, error, "audin_receive reported an error");
79}
80
86static UINT audin_opensles_free(IAudinDevice* device)
87{
88 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
89
90 if (!opensles)
91 return ERROR_INVALID_PARAMETER;
92
93 WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*)device);
94
95 free(opensles->device_name);
96 free(opensles);
97 return CHANNEL_RC_OK;
98}
99
100static BOOL audin_opensles_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
101{
102 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
103
104 if (!opensles || !format)
105 return FALSE;
106
107 WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p", (void*)opensles, (void*)format);
108 WINPR_ASSERT(format);
109
110 switch (format->wFormatTag)
111 {
112 case WAVE_FORMAT_PCM: /* PCM */
113 if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
114 (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
115 (format->nChannels >= 1 && format->nChannels <= 2))
116 {
117 return TRUE;
118 }
119
120 break;
121
122 default:
123 WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04" PRIX16 "] not supported",
124 audio_format_get_tag_string(format->wFormatTag), format->wFormatTag);
125 break;
126 }
127
128 return FALSE;
129}
130
136static UINT audin_opensles_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
137 UINT32 FramesPerPacket)
138{
139 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
140
141 if (!opensles || !format)
142 return ERROR_INVALID_PARAMETER;
143
144 WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p, FramesPerPacket=%" PRIu32 "",
145 (void*)device, (void*)format, FramesPerPacket);
146 WINPR_ASSERT(format);
147
148 opensles->format = *format;
149
150 switch (format->wFormatTag)
151 {
152 case WAVE_FORMAT_PCM:
153 opensles->frames_per_packet = FramesPerPacket;
154
155 switch (format->wBitsPerSample)
156 {
157 case 4:
158 opensles->bytes_per_channel = 1;
159 break;
160
161 case 8:
162 opensles->bytes_per_channel = 1;
163 break;
164
165 case 16:
166 opensles->bytes_per_channel = 2;
167 break;
168
169 default:
170 return ERROR_UNSUPPORTED_TYPE;
171 }
172
173 break;
174
175 default:
176 WLog_Print(opensles->log, WLOG_ERROR,
177 "Encoding '%" PRIu16 "' [%04" PRIX16 "] not supported", format->wFormatTag,
178 format->wFormatTag);
179 return ERROR_UNSUPPORTED_TYPE;
180 }
181
182 WLog_Print(opensles->log, WLOG_DEBUG, "frames_per_packet=%" PRIu32,
183 opensles->frames_per_packet);
184 return CHANNEL_RC_OK;
185}
186
192static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void* user_data)
193{
194 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
195
196 if (!opensles || !receive || !user_data)
197 return ERROR_INVALID_PARAMETER;
198
199 WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, receive=%p, user_data=%p", (void*)device,
200 (void*)receive, (void*)user_data);
201
202 if (opensles->stream)
203 goto error_out;
204
205 if (!(opensles->stream = android_OpenRecDevice(
206 opensles, audin_receive, opensles->format.nSamplesPerSec, opensles->format.nChannels,
207 opensles->frames_per_packet, opensles->format.wBitsPerSample)))
208 {
209 WLog_Print(opensles->log, WLOG_ERROR, "android_OpenRecDevice failed!");
210 goto error_out;
211 }
212
213 opensles->receive = receive;
214 opensles->user_data = user_data;
215 return CHANNEL_RC_OK;
216error_out:
217 audin_opensles_close(device);
218 return ERROR_INTERNAL_ERROR;
219}
220
226UINT audin_opensles_close(IAudinDevice* device)
227{
228 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
229
230 if (!opensles)
231 return ERROR_INVALID_PARAMETER;
232
233 WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*)device);
234 android_CloseRecDevice(opensles->stream);
235 opensles->receive = NULL;
236 opensles->user_data = NULL;
237 opensles->stream = NULL;
238 return CHANNEL_RC_OK;
239}
240
246static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, const ADDIN_ARGV* args)
247{
248 UINT status;
249 DWORD flags;
250 const COMMAND_LINE_ARGUMENT_A* arg;
251 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
252 COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = {
253 { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL,
254 "audio device name" },
255 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
256 };
257
258 WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, args=%p", (void*)device, (void*)args);
259 flags =
260 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
261 status = CommandLineParseArgumentsA(args->argc, args->argv, audin_opensles_args, flags,
262 opensles, NULL, NULL);
263
264 if (status < 0)
265 return status;
266
267 arg = audin_opensles_args;
268
269 do
270 {
271 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
272 continue;
273
274 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
275 {
276 opensles->device_name = _strdup(arg->Value);
277
278 if (!opensles->device_name)
279 {
280 WLog_Print(opensles->log, WLOG_ERROR, "_strdup failed!");
281 return CHANNEL_RC_NO_MEMORY;
282 }
283 }
284 CommandLineSwitchEnd(arg)
285 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
286
287 return CHANNEL_RC_OK;
288}
289
295FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_audin_client_subsystem_entry(
297{
298 UINT error = ERROR_INTERNAL_ERROR;
299 AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)calloc(1, sizeof(AudinOpenSLESDevice));
300
301 if (!opensles)
302 {
303 WLog_ERR(TAG, "calloc failed!");
304 return CHANNEL_RC_NO_MEMORY;
305 }
306
307 opensles->log = WLog_Get(TAG);
308 opensles->iface.Open = audin_opensles_open;
309 opensles->iface.FormatSupported = audin_opensles_format_supported;
310 opensles->iface.SetFormat = audin_opensles_set_format;
311 opensles->iface.Close = audin_opensles_close;
312 opensles->iface.Free = audin_opensles_free;
313 opensles->rdpcontext = pEntryPoints->rdpcontext;
314 const ADDIN_ARGV* args = pEntryPoints->args;
315
316 if ((error = audin_opensles_parse_addin_args(opensles, args)))
317 {
318 WLog_Print(opensles->log, WLOG_ERROR,
319 "audin_opensles_parse_addin_args failed with errorcode %" PRIu32 "!", error);
320 goto error_out;
321 }
322
323 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)opensles)))
324 {
325 WLog_Print(opensles->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
326 error);
327 goto error_out;
328 }
329
330 return CHANNEL_RC_OK;
331error_out:
332 free(opensles);
333 return error;
334}