FreeRDP
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 
42 typedef 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 
62 static UINT audin_opensles_close(IAudinDevice* device);
63 
64 static 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 
86 static 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 
100 static 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 
136 static 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 
192 static 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;
216 error_out:
217  audin_opensles_close(device);
218  return ERROR_INTERNAL_ERROR;
219 }
220 
226 UINT 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 
246 static 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 
295 FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_audin_client_subsystem_entry(
297 {
298  const ADDIN_ARGV* args;
299  AudinOpenSLESDevice* opensles;
300  UINT error;
301  opensles = (AudinOpenSLESDevice*)calloc(1, sizeof(AudinOpenSLESDevice));
302 
303  if (!opensles)
304  {
305  WLog_ERR(TAG, "calloc failed!");
306  return CHANNEL_RC_NO_MEMORY;
307  }
308 
309  opensles->log = WLog_Get(TAG);
310  opensles->iface.Open = audin_opensles_open;
311  opensles->iface.FormatSupported = audin_opensles_format_supported;
312  opensles->iface.SetFormat = audin_opensles_set_format;
313  opensles->iface.Close = audin_opensles_close;
314  opensles->iface.Free = audin_opensles_free;
315  opensles->rdpcontext = pEntryPoints->rdpcontext;
316  args = pEntryPoints->args;
317 
318  if ((error = audin_opensles_parse_addin_args(opensles, args)))
319  {
320  WLog_Print(opensles->log, WLOG_ERROR,
321  "audin_opensles_parse_addin_args failed with errorcode %" PRIu32 "!", error);
322  goto error_out;
323  }
324 
325  if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)opensles)))
326  {
327  WLog_Print(opensles->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
328  error);
329  goto error_out;
330  }
331 
332  return CHANNEL_RC_OK;
333 error_out:
334  free(opensles);
335  return error;
336 }
Definition: client/audin.h:59