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