Merge branch 'master' of github.com:FreeRDP/FreeRDP
[platform/upstream/freerdp.git] / channels / rdpsnd / client / rdpsnd_main.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Audio Output Virtual Channel
4  *
5  * Copyright 2009-2011 Jay Sorg
6  * Copyright 2010-2011 Vic Lee
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #ifndef _WIN32
26 #include <sys/time.h>
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <winpr/crt.h>
34 #include <winpr/synch.h>
35 #include <winpr/print.h>
36 #include <winpr/cmdline.h>
37 #include <winpr/sysinfo.h>
38 #include <winpr/collections.h>
39
40 #include <freerdp/types.h>
41 #include <freerdp/addin.h>
42 #include <freerdp/constants.h>
43 #include <freerdp/utils/stream.h>
44 #include <freerdp/utils/signal.h>
45 #include <freerdp/utils/svc_plugin.h>
46
47 #include "rdpsnd_main.h"
48
49 #define TIME_DELAY_MS   65
50
51 struct rdpsnd_plugin
52 {
53         rdpSvcPlugin plugin;
54
55         HANDLE thread;
56         wMessageQueue* queue;
57
58         BYTE cBlockNo;
59         int wCurrentFormatNo;
60
61         AUDIO_FORMAT* ServerFormats;
62         UINT16 NumberOfServerFormats;
63
64         AUDIO_FORMAT* ClientFormats;
65         UINT16 NumberOfClientFormats;
66
67         BOOL expectingWave;
68         BYTE waveData[4];
69         UINT16 waveDataSize;
70         UINT32 wTimeStamp;
71
72         BOOL isOpen;
73         int latency;
74         UINT16 fixedFormat;
75         UINT16 fixedChannel;
76         UINT32 fixedRate;
77
78         char* subsystem;
79         char* device_name;
80
81         /* Device plugin */
82         rdpsndDevicePlugin* device;
83 };
84
85 void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo);
86
87 static void* rdpsnd_schedule_thread(void* arg)
88 {
89         wMessage message;
90         UINT16 wTimeDiff;
91         UINT16 wTimeStamp;
92         UINT16 wCurrentTime;
93         RDPSND_WAVE* wave;
94         rdpsndPlugin* rdpsnd = (rdpsndPlugin*) arg;
95
96         while (1)
97         {
98                 if (!MessageQueue_Wait(rdpsnd->queue))
99                         break;
100
101                 if (!MessageQueue_Peek(rdpsnd->queue, &message, TRUE))
102                         break;
103
104                 if (message.id == WMQ_QUIT)
105                         break;
106
107                 wave = (RDPSND_WAVE*) message.wParam;
108                 wCurrentTime = (UINT16) GetTickCount();
109                 wTimeStamp = wave->wLocalTimeB;
110
111                 if (wCurrentTime <= wTimeStamp)
112                 {
113                         wTimeDiff = wTimeStamp - wCurrentTime;
114                         Sleep(wTimeDiff);
115                 }
116
117                 rdpsnd_send_wave_confirm_pdu(rdpsnd, wave->wTimeStampB, wave->cBlockNo);
118                 free(wave);
119         }
120
121         return NULL;
122 }
123
124 void rdpsnd_send_quality_mode_pdu(rdpsndPlugin* rdpsnd)
125 {
126         STREAM* pdu;
127
128         pdu = stream_new(8);
129         stream_write_BYTE(pdu, SNDC_QUALITYMODE); /* msgType */
130         stream_write_BYTE(pdu, 0); /* bPad */
131         stream_write_UINT16(pdu, 4); /* BodySize */
132         stream_write_UINT16(pdu, HIGH_QUALITY); /* wQualityMode */
133         stream_write_UINT16(pdu, 0); /* Reserved */
134
135         svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
136 }
137
138 void rdpsnd_free_audio_formats(AUDIO_FORMAT* formats, UINT16 count)
139 {
140         int index;
141         AUDIO_FORMAT* format;
142
143         if (formats)
144         {
145                 for (index = 0; index < (int) count; index++)
146                 {
147                         format = &formats[index];
148
149                         if (format->cbSize)
150                                 free(format->data);
151                 }
152
153                 free(formats);
154         }
155 }
156
157 char* rdpsnd_get_audio_tag_string(UINT16 wFormatTag)
158 {
159         switch (wFormatTag)
160         {
161                 case WAVE_FORMAT_PCM:
162                         return "WAVE_FORMAT_PCM";
163
164                 case WAVE_FORMAT_ADPCM:
165                         return "WAVE_FORMAT_ADPCM";
166
167                 case WAVE_FORMAT_ALAW:
168                         return "WAVE_FORMAT_ALAW";
169
170                 case WAVE_FORMAT_MULAW:
171                         return "WAVE_FORMAT_MULAW";
172
173                 case WAVE_FORMAT_DVI_ADPCM:
174                         return "WAVE_FORMAT_DVI_ADPCM";
175         }
176
177         return "WAVE_FORMAT_UNKNOWN";
178 }
179
180 void rdpsnd_print_audio_format(AUDIO_FORMAT* format)
181 {
182         printf("%s: wFormatTag: 0x%04X nChannels: %d nSamplesPerSec: %d nAvgBytesPerSec: %d nBlockAlign: %d wBitsPerSample: %d\n",
183                         rdpsnd_get_audio_tag_string(format->wFormatTag), format->wFormatTag,
184                         format->nChannels, format->nSamplesPerSec, format->nAvgBytesPerSec,
185                         format->nBlockAlign, format->wBitsPerSample);
186 }
187
188 UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size)
189 {
190         UINT32 mstime;
191         UINT32 wSamples;
192
193         /**
194          * [MSDN-AUDIOFORMAT]:
195          * http://msdn.microsoft.com/en-us/library/ms713497.aspx
196          */
197
198         wSamples = (size * 8) / format->wBitsPerSample;
199         mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels);
200
201         return mstime;
202 }
203
204 void rdpsnd_select_supported_audio_formats(rdpsndPlugin* rdpsnd)
205 {
206         int index;
207         AUDIO_FORMAT* serverFormat;
208         AUDIO_FORMAT* clientFormat;
209
210         rdpsnd_free_audio_formats(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
211         rdpsnd->NumberOfClientFormats = 0;
212         rdpsnd->ClientFormats = NULL;
213
214         rdpsnd->ClientFormats = (AUDIO_FORMAT*) malloc(sizeof(AUDIO_FORMAT) * rdpsnd->NumberOfServerFormats);
215
216         for (index = 0; index < (int) rdpsnd->NumberOfServerFormats; index++)
217         {
218                 serverFormat = &rdpsnd->ServerFormats[index];
219
220                 if (rdpsnd->fixedFormat > 0 && (rdpsnd->fixedFormat != serverFormat->wFormatTag))
221                         continue;
222
223                 if (rdpsnd->fixedChannel > 0 && (rdpsnd->fixedChannel != serverFormat->nChannels))
224                         continue;
225
226                 if (rdpsnd->fixedRate > 0 && (rdpsnd->fixedRate != serverFormat->nSamplesPerSec))
227                         continue;
228
229                 if (rdpsnd->device && rdpsnd->device->FormatSupported(rdpsnd->device, serverFormat))
230                 {
231                         clientFormat = &rdpsnd->ClientFormats[rdpsnd->NumberOfClientFormats++];
232
233                         CopyMemory(clientFormat, serverFormat, sizeof(AUDIO_FORMAT));
234                         clientFormat->cbSize = 0;
235
236                         if (serverFormat->cbSize > 0)
237                         {
238                                 clientFormat->data = (BYTE*) malloc(serverFormat->cbSize);
239                                 CopyMemory(clientFormat->data, serverFormat->data, serverFormat->cbSize);
240                                 clientFormat->cbSize = serverFormat->cbSize;
241                         }
242                 }
243         }
244 }
245
246 void rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
247 {
248         int index;
249         STREAM* pdu;
250         UINT16 length;
251         UINT32 dwVolume;
252         UINT16 dwVolumeLeft;
253         UINT16 dwVolumeRight;
254         UINT16 wNumberOfFormats;
255         AUDIO_FORMAT* clientFormat;
256
257         dwVolumeLeft = (0xFFFF); /* 100% */
258         dwVolumeRight = (0xFFFF); /* 100% */
259         dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
260
261         wNumberOfFormats = rdpsnd->NumberOfClientFormats;
262
263         length = 4 + 20;
264
265         for (index = 0; index < (int) wNumberOfFormats; index++)
266                 length += (18 + rdpsnd->ClientFormats[index].cbSize);
267
268         pdu = stream_new(length);
269
270         stream_write_BYTE(pdu, SNDC_FORMATS); /* msgType */
271         stream_write_BYTE(pdu, 0); /* bPad */
272         stream_write_UINT16(pdu, length - 4); /* BodySize */
273
274         stream_write_UINT32(pdu, TSSNDCAPS_ALIVE | TSSNDCAPS_VOLUME); /* dwFlags */
275         stream_write_UINT32(pdu, dwVolume); /* dwVolume */
276         stream_write_UINT32(pdu, 0); /* dwPitch */
277         stream_write_UINT16(pdu, 0); /* wDGramPort */
278         stream_write_UINT16(pdu, wNumberOfFormats); /* wNumberOfFormats */
279         stream_write_BYTE(pdu, 0); /* cLastBlockConfirmed */
280         stream_write_UINT16(pdu, 6); /* wVersion */
281         stream_write_BYTE(pdu, 0); /* bPad */
282
283         for (index = 0; index < (int) wNumberOfFormats; index++)
284         {
285                 clientFormat = &rdpsnd->ClientFormats[index];
286
287                 stream_write_UINT16(pdu, clientFormat->wFormatTag);
288                 stream_write_UINT16(pdu, clientFormat->nChannels);
289                 stream_write_UINT32(pdu, clientFormat->nSamplesPerSec);
290                 stream_write_UINT32(pdu, clientFormat->nAvgBytesPerSec);
291                 stream_write_UINT16(pdu, clientFormat->nBlockAlign);
292                 stream_write_UINT16(pdu, clientFormat->wBitsPerSample);
293                 stream_write_UINT16(pdu, clientFormat->cbSize);
294
295                 if (clientFormat->cbSize > 0)
296                         stream_write(pdu, clientFormat->data, clientFormat->cbSize);
297         }
298
299         svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
300 }
301
302 void rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
303 {
304         int index;
305         UINT16 wVersion;
306         AUDIO_FORMAT* format;
307         UINT16 wNumberOfFormats;
308
309         rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
310         rdpsnd->NumberOfServerFormats = 0;
311         rdpsnd->ServerFormats = NULL;
312
313         stream_seek_UINT32(s); /* dwFlags */
314         stream_seek_UINT32(s); /* dwVolume */
315         stream_seek_UINT32(s); /* dwPitch */
316         stream_seek_UINT16(s); /* wDGramPort */
317         stream_read_UINT16(s, wNumberOfFormats);
318         stream_read_BYTE(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
319         stream_read_UINT16(s, wVersion); /* wVersion */
320         stream_seek_BYTE(s); /* bPad */
321
322         rdpsnd->NumberOfServerFormats = wNumberOfFormats;
323         rdpsnd->ServerFormats = (AUDIO_FORMAT*) malloc(sizeof(AUDIO_FORMAT) * wNumberOfFormats);
324
325         for (index = 0; index < (int) wNumberOfFormats; index++)
326         {
327                 format = &rdpsnd->ServerFormats[index];
328
329                 stream_read_UINT16(s, format->wFormatTag); /* wFormatTag */
330                 stream_read_UINT16(s, format->nChannels); /* nChannels */
331                 stream_read_UINT32(s, format->nSamplesPerSec); /* nSamplesPerSec */
332                 stream_read_UINT32(s, format->nAvgBytesPerSec); /* nAvgBytesPerSec */
333                 stream_read_UINT16(s, format->nBlockAlign); /* nBlockAlign */
334                 stream_read_UINT16(s, format->wBitsPerSample); /* wBitsPerSample */
335                 stream_read_UINT16(s, format->cbSize); /* cbSize */
336
337                 format->data = (BYTE*) malloc(format->cbSize);
338                 stream_read(s, format->data, format->cbSize);
339         }
340
341         rdpsnd_select_supported_audio_formats(rdpsnd);
342
343         rdpsnd_send_client_audio_formats(rdpsnd);
344
345         if (wVersion >= 6)
346                 rdpsnd_send_quality_mode_pdu(rdpsnd);
347 }
348
349 void rdpsnd_send_training_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, UINT16 wPackSize)
350 {
351         STREAM* pdu;
352
353         pdu = stream_new(8);
354         stream_write_BYTE(pdu, SNDC_TRAINING); /* msgType */
355         stream_write_BYTE(pdu, 0); /* bPad */
356         stream_write_UINT16(pdu, 4); /* BodySize */
357         stream_write_UINT16(pdu, wTimeStamp);
358         stream_write_UINT16(pdu, wPackSize);
359
360         svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
361 }
362
363 static void rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
364 {
365         UINT16 wTimeStamp;
366         UINT16 wPackSize;
367
368         stream_read_UINT16(s, wTimeStamp);
369         stream_read_UINT16(s, wPackSize);
370
371         rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
372 }
373
374 static void rdpsnd_recv_wave_info_pdu(rdpsndPlugin* rdpsnd, STREAM* s, UINT16 BodySize)
375 {
376         UINT16 wFormatNo;
377         AUDIO_FORMAT* format;
378
379         stream_read_UINT16(s, rdpsnd->wTimeStamp);
380         stream_read_UINT16(s, wFormatNo);
381         stream_read_BYTE(s, rdpsnd->cBlockNo);
382         stream_seek(s, 3); /* bPad */
383         stream_read(s, rdpsnd->waveData, 4);
384
385         rdpsnd->waveDataSize = BodySize - 8;
386         rdpsnd->expectingWave = TRUE;
387
388         format = &rdpsnd->ClientFormats[wFormatNo];
389
390         if (!rdpsnd->isOpen)
391         {
392                 rdpsnd->isOpen = TRUE;
393                 rdpsnd->wCurrentFormatNo = wFormatNo;
394
395                 rdpsnd_print_audio_format(format);
396
397                 if (rdpsnd->device)
398                 {
399                         IFCALL(rdpsnd->device->Open, rdpsnd->device, format, rdpsnd->latency);
400                 }
401         }
402         else if (wFormatNo != rdpsnd->wCurrentFormatNo)
403         {
404                 rdpsnd->wCurrentFormatNo = wFormatNo;
405
406                 if (rdpsnd->device)
407                 {
408                         IFCALL(rdpsnd->device->SetFormat, rdpsnd->device, format, rdpsnd->latency);
409                 }
410         }
411 }
412
413 void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo)
414 {
415         STREAM* pdu;
416
417         pdu = stream_new(8);
418         stream_write_BYTE(pdu, SNDC_WAVECONFIRM);
419         stream_write_BYTE(pdu, 0);
420         stream_write_UINT16(pdu, 4);
421         stream_write_UINT16(pdu, wTimeStamp);
422         stream_write_BYTE(pdu, cConfirmedBlockNo); /* cConfirmedBlockNo */
423         stream_write_BYTE(pdu, 0); /* bPad */
424
425         svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
426 }
427
428 void rdpsnd_device_send_wave_confirm_pdu(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
429 {
430         MessageQueue_Post(device->rdpsnd->queue, NULL, 0, (void*) wave, NULL);
431 }
432
433 static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
434 {
435         int size;
436         BYTE* data;
437         RDPSND_WAVE* wave;
438         AUDIO_FORMAT* format;
439
440         rdpsnd->expectingWave = FALSE;
441
442         /**
443          * The Wave PDU is a special case: it is always sent after a Wave Info PDU,
444          * and we do not process its header. Instead, the header is pad that needs
445          * to be filled with the first four bytes of the audio sample data sent as
446          * part of the preceding Wave Info PDU.
447          */
448
449         CopyMemory(stream_get_head(s), rdpsnd->waveData, 4);
450
451         data = stream_get_head(s);
452         size = stream_get_size(s);
453
454         wave = (RDPSND_WAVE*) malloc(sizeof(RDPSND_WAVE));
455
456         wave->wLocalTimeA = GetTickCount();
457         wave->wTimeStampA = rdpsnd->wTimeStamp;
458         wave->wFormatNo = rdpsnd->wCurrentFormatNo;
459         wave->cBlockNo = rdpsnd->cBlockNo;
460
461         wave->data = data;
462         wave->length = size;
463
464         format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
465         wave->wAudioLength = rdpsnd_compute_audio_time_length(format, size);
466
467         if (!rdpsnd->device)
468                 return;
469
470         if (rdpsnd->device->WaveDecode)
471         {
472                 IFCALL(rdpsnd->device->WaveDecode, rdpsnd->device, wave);
473         }
474
475         if (rdpsnd->device->WavePlay)
476         {
477                 IFCALL(rdpsnd->device->WavePlay, rdpsnd->device, wave);
478         }
479         else
480         {
481                 IFCALL(rdpsnd->device->Play, rdpsnd->device, data, size);
482         }
483
484         if (!rdpsnd->device->WavePlay)
485         {
486                 wave->wTimeStampB = rdpsnd->wTimeStamp + wave->wAudioLength + TIME_DELAY_MS;
487                 wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + TIME_DELAY_MS;
488                 rdpsnd->device->WaveConfirm(rdpsnd->device, wave);
489         }
490 }
491
492 static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
493 {
494         DEBUG_SVC("server closes.");
495
496         if (rdpsnd->device)
497         {
498                 IFCALL(rdpsnd->device->Start, rdpsnd->device);
499         }
500 }
501
502 static void rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
503 {
504         UINT32 dwVolume;
505
506         stream_read_UINT32(s, dwVolume);
507         DEBUG_SVC("dwVolume 0x%X", dwVolume);
508
509         if (rdpsnd->device)
510         {
511                 IFCALL(rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
512         }
513 }
514
515 static void rdpsnd_recv_pdu(rdpSvcPlugin* plugin, STREAM* s)
516 {
517         BYTE msgType;
518         UINT16 BodySize;
519         rdpsndPlugin* rdpsnd = (rdpsndPlugin*) plugin;
520
521         if (rdpsnd->expectingWave)
522         {
523                 rdpsnd_recv_wave_pdu(rdpsnd, s);
524                 stream_free(s);
525                 return;
526         }
527
528         stream_read_BYTE(s, msgType); /* msgType */
529         stream_seek_BYTE(s); /* bPad */
530         stream_read_UINT16(s, BodySize);
531
532         DEBUG_SVC("msgType %d BodySize %d", msgType, BodySize);
533
534         switch (msgType)
535         {
536                 case SNDC_FORMATS:
537                         rdpsnd_recv_server_audio_formats_pdu(rdpsnd, s);
538                         break;
539
540                 case SNDC_TRAINING:
541                         rdpsnd_recv_training_pdu(rdpsnd, s);
542                         break;
543
544                 case SNDC_WAVE:
545                         rdpsnd_recv_wave_info_pdu(rdpsnd, s, BodySize);
546                         break;
547
548                 case SNDC_CLOSE:
549                         rdpsnd_recv_close_pdu(rdpsnd);
550                         break;
551
552                 case SNDC_SETVOLUME:
553                         rdpsnd_recv_volume_pdu(rdpsnd, s);
554                         break;
555
556                 default:
557                         DEBUG_WARN("unknown msgType %d", msgType);
558                         break;
559         }
560
561         stream_free(s);
562 }
563
564 static void rdpsnd_register_device_plugin(rdpsndPlugin* rdpsnd, rdpsndDevicePlugin* device)
565 {
566         if (rdpsnd->device)
567         {
568                 DEBUG_WARN("existing device, abort.");
569                 return;
570         }
571
572         rdpsnd->device = device;
573         device->rdpsnd = rdpsnd;
574
575         device->WaveConfirm = rdpsnd_device_send_wave_confirm_pdu;
576 }
577
578 static BOOL rdpsnd_load_device_plugin(rdpsndPlugin* rdpsnd, const char* name, ADDIN_ARGV* args)
579 {
580         PFREERDP_RDPSND_DEVICE_ENTRY entry;
581         FREERDP_RDPSND_DEVICE_ENTRY_POINTS entryPoints;
582
583         entry = (PFREERDP_RDPSND_DEVICE_ENTRY) freerdp_load_channel_addin_entry("rdpsnd", (LPSTR) name, NULL, 0);
584
585         if (!entry)
586                 return FALSE;
587
588         entryPoints.rdpsnd = rdpsnd;
589         entryPoints.pRegisterRdpsndDevice = rdpsnd_register_device_plugin;
590         entryPoints.args = args;
591
592         if (entry(&entryPoints) != 0)
593         {
594                 DEBUG_WARN("%s entry returns error.", name);
595                 return FALSE;
596         }
597
598         return TRUE;
599 }
600
601 void rdpsnd_set_subsystem(rdpsndPlugin* rdpsnd, char* subsystem)
602 {
603         if (rdpsnd->subsystem)
604                 free(rdpsnd->subsystem);
605
606         rdpsnd->subsystem = _strdup(subsystem);
607 }
608
609 void rdpsnd_set_device_name(rdpsndPlugin* rdpsnd, char* device_name)
610 {
611         if (rdpsnd->device_name)
612                 free(rdpsnd->device_name);
613
614         rdpsnd->device_name = _strdup(device_name);
615 }
616
617 COMMAND_LINE_ARGUMENT_A rdpsnd_args[] =
618 {
619         { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" },
620         { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
621         { "format", COMMAND_LINE_VALUE_REQUIRED, "<format>", NULL, NULL, -1, NULL, "format" },
622         { "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", NULL, NULL, -1, NULL, "rate" },
623         { "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", NULL, NULL, -1, NULL, "channel" },
624         { "latency", COMMAND_LINE_VALUE_REQUIRED, "<latency>", NULL, NULL, -1, NULL, "latency" },
625         { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
626 };
627
628 static void rdpsnd_process_addin_args(rdpsndPlugin* rdpsnd, ADDIN_ARGV* args)
629 {
630         int status;
631         DWORD flags;
632         COMMAND_LINE_ARGUMENT_A* arg;
633
634         flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
635
636         status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
637                         rdpsnd_args, flags, rdpsnd, NULL, NULL);
638
639         arg = rdpsnd_args;
640
641         do
642         {
643                 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
644                         continue;
645
646                 CommandLineSwitchStart(arg)
647
648                 CommandLineSwitchCase(arg, "sys")
649                 {
650                         rdpsnd_set_subsystem(rdpsnd, arg->Value);
651                 }
652                 CommandLineSwitchCase(arg, "dev")
653                 {
654                         rdpsnd_set_device_name(rdpsnd, arg->Value);
655                 }
656                 CommandLineSwitchCase(arg, "format")
657                 {
658                         rdpsnd->fixedFormat = atoi(arg->Value);
659                 }
660                 CommandLineSwitchCase(arg, "rate")
661                 {
662                         rdpsnd->fixedRate = atoi(arg->Value);
663                 }
664                 CommandLineSwitchCase(arg, "channel")
665                 {
666                         rdpsnd->fixedChannel = atoi(arg->Value);
667                 }
668                 CommandLineSwitchCase(arg, "latency")
669                 {
670                         rdpsnd->latency = atoi(arg->Value);
671                 }
672                 CommandLineSwitchDefault(arg)
673                 {
674
675                 }
676
677                 CommandLineSwitchEnd(arg)
678         }
679         while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
680 }
681
682 static void rdpsnd_process_connect(rdpSvcPlugin* plugin)
683 {
684         ADDIN_ARGV* args;
685         rdpsndPlugin* rdpsnd = (rdpsndPlugin*) plugin;
686
687         DEBUG_SVC("connecting");
688
689         rdpsnd->latency = -1;
690         rdpsnd->queue = MessageQueue_New();
691         rdpsnd->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread, (void*) plugin, 0, NULL);
692
693         args = (ADDIN_ARGV*) plugin->channel_entry_points.pExtendedData;
694
695         if (args)
696                 rdpsnd_process_addin_args(rdpsnd, args);
697
698         if (rdpsnd->subsystem)
699         {
700                 if (strcmp(rdpsnd->subsystem, "fake") == 0)
701                         return;
702
703                 rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
704         }
705
706         if (!rdpsnd->device)
707         {
708                 rdpsnd_set_subsystem(rdpsnd, "pulse");
709                 rdpsnd_set_device_name(rdpsnd, "");
710                 rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
711         }
712
713         if (!rdpsnd->device)
714         {
715                 rdpsnd_set_subsystem(rdpsnd, "alsa");
716                 rdpsnd_set_device_name(rdpsnd, "default");
717                 rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
718         }
719
720         if (!rdpsnd->device)
721         {
722                 rdpsnd_set_subsystem(rdpsnd, "macaudio");
723                 rdpsnd_set_device_name(rdpsnd, "default");
724                 rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
725         }
726
727         if (!rdpsnd->device)
728         {
729                 rdpsnd_set_subsystem(rdpsnd, "winmm");
730                 rdpsnd_set_device_name(rdpsnd, "");
731                 rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
732         }
733
734         if (!rdpsnd->device)
735         {
736                 DEBUG_WARN("no sound device.");
737                 return;
738         }
739 }
740
741 static void rdpsnd_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
742 {
743         freerdp_event_free(event);
744 }
745
746 static void rdpsnd_process_terminate(rdpSvcPlugin* plugin)
747 {
748         rdpsndPlugin* rdpsnd = (rdpsndPlugin*) plugin;
749
750         if (rdpsnd->device)
751                 IFCALL(rdpsnd->device->Free, rdpsnd->device);
752
753         MessageQueue_PostQuit(rdpsnd->queue, 0);
754         WaitForSingleObject(rdpsnd->thread, INFINITE);
755
756         MessageQueue_Free(rdpsnd->queue);
757         CloseHandle(rdpsnd->thread);
758
759         if (rdpsnd->subsystem)
760                 free(rdpsnd->subsystem);
761
762         if (rdpsnd->device_name)
763                 free(rdpsnd->device_name);
764
765         rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
766         rdpsnd->NumberOfServerFormats = 0;
767         rdpsnd->ServerFormats = NULL;
768
769         rdpsnd_free_audio_formats(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
770         rdpsnd->NumberOfClientFormats = 0;
771         rdpsnd->ClientFormats = NULL;
772 }
773
774 /* rdpsnd is always built-in */
775 #define VirtualChannelEntry     rdpsnd_VirtualChannelEntry
776
777 int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
778 {
779         rdpsndPlugin* _p;
780
781         _p = (rdpsndPlugin*) malloc(sizeof(rdpsndPlugin));
782         ZeroMemory(_p, sizeof(rdpsndPlugin));
783
784         _p->plugin.channel_def.options =
785                         CHANNEL_OPTION_INITIALIZED |
786                         CHANNEL_OPTION_ENCRYPT_RDP;
787
788         strcpy(_p->plugin.channel_def.name, "rdpsnd");
789
790         _p->plugin.connect_callback = rdpsnd_process_connect;
791         _p->plugin.receive_callback = rdpsnd_recv_pdu;
792         _p->plugin.event_callback = rdpsnd_process_event;
793         _p->plugin.terminate_callback = rdpsnd_process_terminate;
794
795 #ifndef _WIN32
796         {
797                 sigset_t mask;
798                 sigemptyset(&mask);
799                 sigaddset(&mask, SIGIO);
800                 pthread_sigmask(SIG_BLOCK, &mask, NULL);
801         }
802 #endif
803
804         svc_plugin_init((rdpSvcPlugin*) _p, pEntryPoints);
805
806         return 1;
807 }