1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * Video Redirection Virtual Channel
4 *
5 * Copyright 2010-2011 Vic Lee
6 * Copyright 2015 Thincast Technologies GmbH
7 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <winpr/crt.h>
27 #include <winpr/stream.h>
28 #include <winpr/cmdline.h>
29
30 #include <freerdp/client/tsmf.h>
31
32 #include "tsmf_types.h"
33 #include "tsmf_constants.h"
34 #include "tsmf_ifman.h"
35 #include "tsmf_media.h"
36
37 #include "tsmf_main.h"
38
tsmf_send_eos_response(IWTSVirtualChannelCallback * pChannelCallback,UINT32 message_id)39 BOOL tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id)
40 {
41 wStream* s = NULL;
42 int status = -1;
43 TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
44
45 if (!callback)
46 {
47 DEBUG_TSMF("No callback reference - unable to send eos response!");
48 return FALSE;
49 }
50
51 if (callback && callback->stream_id && callback->channel && callback->channel->Write)
52 {
53 s = Stream_New(NULL, 24);
54
55 if (!s)
56 return FALSE;
57
58 Stream_Write_UINT32(s, TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY);
59 Stream_Write_UINT32(s, message_id);
60 Stream_Write_UINT32(s, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
61 Stream_Write_UINT32(s, callback->stream_id); /* StreamId */
62 Stream_Write_UINT32(s, TSMM_CLIENT_EVENT_ENDOFSTREAM); /* EventId */
63 Stream_Write_UINT32(s, 0); /* cbData */
64 DEBUG_TSMF("EOS response size %" PRIuz "", Stream_GetPosition(s));
65 status = callback->channel->Write(callback->channel, Stream_GetPosition(s),
66 Stream_Buffer(s), NULL);
67
68 if (status)
69 {
70 WLog_ERR(TAG, "response error %d", status);
71 }
72
73 Stream_Free(s, TRUE);
74 }
75
76 return (status == 0);
77 }
78
tsmf_playback_ack(IWTSVirtualChannelCallback * pChannelCallback,UINT32 message_id,UINT64 duration,UINT32 data_size)79 BOOL tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id,
80 UINT64 duration, UINT32 data_size)
81 {
82 wStream* s = NULL;
83 int status = -1;
84 TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
85
86 if (!callback)
87 return FALSE;
88
89 s = Stream_New(NULL, 32);
90
91 if (!s)
92 return FALSE;
93
94 Stream_Write_UINT32(s, TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY);
95 Stream_Write_UINT32(s, message_id);
96 Stream_Write_UINT32(s, PLAYBACK_ACK); /* FunctionId */
97 Stream_Write_UINT32(s, callback->stream_id); /* StreamId */
98 Stream_Write_UINT64(s, duration); /* DataDuration */
99 Stream_Write_UINT64(s, data_size); /* cbData */
100 DEBUG_TSMF("ACK response size %" PRIuz "", Stream_GetPosition(s));
101
102 if (!callback->channel || !callback->channel->Write)
103 {
104 WLog_ERR(TAG, "callback=%p, channel=%p, write=%p", callback,
105 (callback ? callback->channel : NULL),
106 (callback && callback->channel ? callback->channel->Write : NULL));
107 }
108 else
109 {
110 status = callback->channel->Write(callback->channel, Stream_GetPosition(s),
111 Stream_Buffer(s), NULL);
112 }
113
114 if (status)
115 {
116 WLog_ERR(TAG, "response error %d", status);
117 }
118
119 Stream_Free(s, TRUE);
120 return (status == 0);
121 }
122
123 /**
124 * Function description
125 *
126 * @return 0 on success, otherwise a Win32 error code
127 */
tsmf_on_data_received(IWTSVirtualChannelCallback * pChannelCallback,wStream * data)128 static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
129 {
130 size_t length;
131 wStream* input;
132 wStream* output;
133 UINT error = CHANNEL_RC_OK;
134 BOOL processed = FALSE;
135 TSMF_IFMAN ifman;
136 UINT32 MessageId;
137 UINT32 FunctionId;
138 UINT32 InterfaceId;
139 TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
140 UINT32 cbSize = Stream_GetRemainingLength(data);
141
142 /* 2.2.1 Shared Message Header (SHARED_MSG_HEADER) */
143 if (cbSize < 12)
144 {
145 WLog_ERR(TAG, "invalid size. cbSize=%" PRIu32 "", cbSize);
146 return ERROR_INVALID_DATA;
147 }
148
149 input = data;
150 output = Stream_New(NULL, 256);
151
152 if (!output)
153 return ERROR_OUTOFMEMORY;
154
155 Stream_Seek(output, 8);
156 Stream_Read_UINT32(input, InterfaceId); /* InterfaceId (4 bytes) */
157 Stream_Read_UINT32(input, MessageId); /* MessageId (4 bytes) */
158 Stream_Read_UINT32(input, FunctionId); /* FunctionId (4 bytes) */
159 DEBUG_TSMF("cbSize=%" PRIu32 " InterfaceId=0x%" PRIX32 " MessageId=0x%" PRIX32
160 " FunctionId=0x%" PRIX32 "",
161 cbSize, InterfaceId, MessageId, FunctionId);
162 ZeroMemory(&ifman, sizeof(TSMF_IFMAN));
163 ifman.channel_callback = pChannelCallback;
164 ifman.decoder_name = ((TSMF_PLUGIN*)callback->plugin)->decoder_name;
165 ifman.audio_name = ((TSMF_PLUGIN*)callback->plugin)->audio_name;
166 ifman.audio_device = ((TSMF_PLUGIN*)callback->plugin)->audio_device;
167 CopyMemory(ifman.presentation_id, callback->presentation_id, GUID_SIZE);
168 ifman.stream_id = callback->stream_id;
169 ifman.message_id = MessageId;
170 ifman.input = input;
171 ifman.input_size = cbSize - 12;
172 ifman.output = output;
173 ifman.output_pending = FALSE;
174 ifman.output_interface_id = InterfaceId;
175
176 // fprintf(stderr, "InterfaceId: 0x%08"PRIX32" MessageId: 0x%08"PRIX32" FunctionId:
177 // 0x%08"PRIX32"\n", InterfaceId, MessageId, FunctionId);
178
179 switch (InterfaceId)
180 {
181 case TSMF_INTERFACE_CAPABILITIES | STREAM_ID_NONE:
182 switch (FunctionId)
183 {
184 case RIM_EXCHANGE_CAPABILITY_REQUEST:
185 error = tsmf_ifman_rim_exchange_capability_request(&ifman);
186 processed = TRUE;
187 break;
188
189 case RIMCALL_RELEASE:
190 case RIMCALL_QUERYINTERFACE:
191 break;
192
193 default:
194 break;
195 }
196
197 break;
198
199 case TSMF_INTERFACE_DEFAULT | STREAM_ID_PROXY:
200 switch (FunctionId)
201 {
202 case SET_CHANNEL_PARAMS:
203 if (Stream_GetRemainingLength(input) < GUID_SIZE + 4)
204 {
205 error = ERROR_INVALID_DATA;
206 goto out;
207 }
208
209 CopyMemory(callback->presentation_id, Stream_Pointer(input), GUID_SIZE);
210 Stream_Seek(input, GUID_SIZE);
211 Stream_Read_UINT32(input, callback->stream_id);
212 DEBUG_TSMF("SET_CHANNEL_PARAMS StreamId=%" PRIu32 "", callback->stream_id);
213 ifman.output_pending = TRUE;
214 processed = TRUE;
215 break;
216
217 case EXCHANGE_CAPABILITIES_REQ:
218 error = tsmf_ifman_exchange_capability_request(&ifman);
219 processed = TRUE;
220 break;
221
222 case CHECK_FORMAT_SUPPORT_REQ:
223 error = tsmf_ifman_check_format_support_request(&ifman);
224 processed = TRUE;
225 break;
226
227 case ON_NEW_PRESENTATION:
228 error = tsmf_ifman_on_new_presentation(&ifman);
229 processed = TRUE;
230 break;
231
232 case ADD_STREAM:
233 error =
234 tsmf_ifman_add_stream(&ifman, ((TSMF_PLUGIN*)callback->plugin)->rdpcontext);
235 processed = TRUE;
236 break;
237
238 case SET_TOPOLOGY_REQ:
239 error = tsmf_ifman_set_topology_request(&ifman);
240 processed = TRUE;
241 break;
242
243 case REMOVE_STREAM:
244 error = tsmf_ifman_remove_stream(&ifman);
245 processed = TRUE;
246 break;
247
248 case SET_SOURCE_VIDEO_RECT:
249 error = tsmf_ifman_set_source_video_rect(&ifman);
250 processed = TRUE;
251 break;
252
253 case SHUTDOWN_PRESENTATION_REQ:
254 error = tsmf_ifman_shutdown_presentation(&ifman);
255 processed = TRUE;
256 break;
257
258 case ON_STREAM_VOLUME:
259 error = tsmf_ifman_on_stream_volume(&ifman);
260 processed = TRUE;
261 break;
262
263 case ON_CHANNEL_VOLUME:
264 error = tsmf_ifman_on_channel_volume(&ifman);
265 processed = TRUE;
266 break;
267
268 case SET_VIDEO_WINDOW:
269 error = tsmf_ifman_set_video_window(&ifman);
270 processed = TRUE;
271 break;
272
273 case UPDATE_GEOMETRY_INFO:
274 error = tsmf_ifman_update_geometry_info(&ifman);
275 processed = TRUE;
276 break;
277
278 case SET_ALLOCATOR:
279 error = tsmf_ifman_set_allocator(&ifman);
280 processed = TRUE;
281 break;
282
283 case NOTIFY_PREROLL:
284 error = tsmf_ifman_notify_preroll(&ifman);
285 processed = TRUE;
286 break;
287
288 case ON_SAMPLE:
289 error = tsmf_ifman_on_sample(&ifman);
290 processed = TRUE;
291 break;
292
293 case ON_FLUSH:
294 error = tsmf_ifman_on_flush(&ifman);
295 processed = TRUE;
296 break;
297
298 case ON_END_OF_STREAM:
299 error = tsmf_ifman_on_end_of_stream(&ifman);
300 processed = TRUE;
301 break;
302
303 case ON_PLAYBACK_STARTED:
304 error = tsmf_ifman_on_playback_started(&ifman);
305 processed = TRUE;
306 break;
307
308 case ON_PLAYBACK_PAUSED:
309 error = tsmf_ifman_on_playback_paused(&ifman);
310 processed = TRUE;
311 break;
312
313 case ON_PLAYBACK_RESTARTED:
314 error = tsmf_ifman_on_playback_restarted(&ifman);
315 processed = TRUE;
316 break;
317
318 case ON_PLAYBACK_STOPPED:
319 error = tsmf_ifman_on_playback_stopped(&ifman);
320 processed = TRUE;
321 break;
322
323 case ON_PLAYBACK_RATE_CHANGED:
324 error = tsmf_ifman_on_playback_rate_changed(&ifman);
325 processed = TRUE;
326 break;
327
328 case RIMCALL_RELEASE:
329 case RIMCALL_QUERYINTERFACE:
330 break;
331
332 default:
333 break;
334 }
335
336 break;
337
338 default:
339 break;
340 }
341
342 input = NULL;
343 ifman.input = NULL;
344
345 if (error)
346 {
347 WLog_ERR(TAG, "ifman data received processing error %" PRIu32 "", error);
348 }
349
350 if (!processed)
351 {
352 switch (FunctionId)
353 {
354 case RIMCALL_RELEASE:
355 /* [MS-RDPEXPS] 2.2.2.2 Interface Release (IFACE_RELEASE)
356 This message does not require a reply. */
357 processed = TRUE;
358 ifman.output_pending = 1;
359 break;
360
361 case RIMCALL_QUERYINTERFACE:
362 /* [MS-RDPEXPS] 2.2.2.1.2 Query Interface Response (QI_RSP)
363 This message is not supported in this channel. */
364 processed = TRUE;
365 break;
366 }
367
368 if (!processed)
369 {
370 WLog_ERR(TAG,
371 "Unknown InterfaceId: 0x%08" PRIX32 " MessageId: 0x%08" PRIX32
372 " FunctionId: 0x%08" PRIX32 "\n",
373 InterfaceId, MessageId, FunctionId);
374 /* When a request is not implemented we return empty response indicating error */
375 }
376
377 processed = TRUE;
378 }
379
380 if (processed && !ifman.output_pending)
381 {
382 /* Response packet does not have FunctionId */
383 length = Stream_GetPosition(output);
384 Stream_SetPosition(output, 0);
385 Stream_Write_UINT32(output, ifman.output_interface_id);
386 Stream_Write_UINT32(output, MessageId);
387 DEBUG_TSMF("response size %d", length);
388 error = callback->channel->Write(callback->channel, length, Stream_Buffer(output), NULL);
389
390 if (error)
391 {
392 WLog_ERR(TAG, "response error %" PRIu32 "", error);
393 }
394 }
395
396 out:
397 Stream_Free(output, TRUE);
398 return error;
399 }
400
401 /**
402 * Function description
403 *
404 * @return 0 on success, otherwise a Win32 error code
405 */
tsmf_on_close(IWTSVirtualChannelCallback * pChannelCallback)406 static UINT tsmf_on_close(IWTSVirtualChannelCallback* pChannelCallback)
407 {
408 TSMF_STREAM* stream;
409 TSMF_PRESENTATION* presentation;
410 TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*)pChannelCallback;
411 DEBUG_TSMF("");
412
413 if (callback->stream_id)
414 {
415 presentation = tsmf_presentation_find_by_id(callback->presentation_id);
416
417 if (presentation)
418 {
419 stream = tsmf_stream_find_by_id(presentation, callback->stream_id);
420
421 if (stream)
422 tsmf_stream_free(stream);
423 }
424 }
425
426 free(pChannelCallback);
427 return CHANNEL_RC_OK;
428 }
429
430 /**
431 * Function description
432 *
433 * @return 0 on success, otherwise a Win32 error code
434 */
tsmf_on_new_channel_connection(IWTSListenerCallback * pListenerCallback,IWTSVirtualChannel * pChannel,BYTE * Data,BOOL * pbAccept,IWTSVirtualChannelCallback ** ppCallback)435 static UINT tsmf_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
436 IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
437 IWTSVirtualChannelCallback** ppCallback)
438 {
439 TSMF_CHANNEL_CALLBACK* callback;
440 TSMF_LISTENER_CALLBACK* listener_callback = (TSMF_LISTENER_CALLBACK*)pListenerCallback;
441 DEBUG_TSMF("");
442 callback = (TSMF_CHANNEL_CALLBACK*)calloc(1, sizeof(TSMF_CHANNEL_CALLBACK));
443
444 if (!callback)
445 return CHANNEL_RC_NO_MEMORY;
446
447 callback->iface.OnDataReceived = tsmf_on_data_received;
448 callback->iface.OnClose = tsmf_on_close;
449 callback->iface.OnOpen = NULL;
450 callback->plugin = listener_callback->plugin;
451 callback->channel_mgr = listener_callback->channel_mgr;
452 callback->channel = pChannel;
453 *ppCallback = (IWTSVirtualChannelCallback*)callback;
454 return CHANNEL_RC_OK;
455 }
456
457 /**
458 * Function description
459 *
460 * @return 0 on success, otherwise a Win32 error code
461 */
tsmf_plugin_initialize(IWTSPlugin * pPlugin,IWTSVirtualChannelManager * pChannelMgr)462 static UINT tsmf_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
463 {
464 UINT status;
465 TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*)pPlugin;
466 DEBUG_TSMF("");
467 tsmf->listener_callback = (TSMF_LISTENER_CALLBACK*)calloc(1, sizeof(TSMF_LISTENER_CALLBACK));
468
469 if (!tsmf->listener_callback)
470 return CHANNEL_RC_NO_MEMORY;
471
472 tsmf->listener_callback->iface.OnNewChannelConnection = tsmf_on_new_channel_connection;
473 tsmf->listener_callback->plugin = pPlugin;
474 tsmf->listener_callback->channel_mgr = pChannelMgr;
475 status = pChannelMgr->CreateListener(
476 pChannelMgr, "TSMF", 0, (IWTSListenerCallback*)tsmf->listener_callback, &(tsmf->listener));
477 tsmf->listener->pInterface = tsmf->iface.pInterface;
478 return status;
479 }
480
481 /**
482 * Function description
483 *
484 * @return 0 on success, otherwise a Win32 error code
485 */
tsmf_plugin_terminated(IWTSPlugin * pPlugin)486 static UINT tsmf_plugin_terminated(IWTSPlugin* pPlugin)
487 {
488 TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*)pPlugin;
489 DEBUG_TSMF("");
490 free(tsmf->listener_callback);
491 free(tsmf);
492 return CHANNEL_RC_OK;
493 }
494
495 /**
496 * Function description
497 *
498 * @return 0 on success, otherwise a Win32 error code
499 */
tsmf_process_addin_args(IWTSPlugin * pPlugin,ADDIN_ARGV * args)500 static UINT tsmf_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
501 {
502 int status;
503 DWORD flags;
504 COMMAND_LINE_ARGUMENT_A* arg;
505 TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*)pPlugin;
506 COMMAND_LINE_ARGUMENT_A tsmf_args[] = { { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>",
507 NULL, NULL, -1, NULL, "audio subsystem" },
508 { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL,
509 NULL, -1, NULL, "audio device name" },
510 { "decoder", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>",
511 NULL, NULL, -1, NULL, "decoder subsystem" },
512 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
513 flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
514 status = CommandLineParseArgumentsA(args->argc, args->argv, tsmf_args, flags, tsmf, NULL, NULL);
515
516 if (status != 0)
517 return ERROR_INVALID_DATA;
518
519 arg = tsmf_args;
520
521 do
522 {
523 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
524 continue;
525
526 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
527 {
528 tsmf->audio_name = _strdup(arg->Value);
529
530 if (!tsmf->audio_name)
531 return ERROR_OUTOFMEMORY;
532 }
533 CommandLineSwitchCase(arg, "dev")
534 {
535 tsmf->audio_device = _strdup(arg->Value);
536
537 if (!tsmf->audio_device)
538 return ERROR_OUTOFMEMORY;
539 }
540 CommandLineSwitchCase(arg, "decoder")
541 {
542 tsmf->decoder_name = _strdup(arg->Value);
543
544 if (!tsmf->decoder_name)
545 return ERROR_OUTOFMEMORY;
546 }
547 CommandLineSwitchDefault(arg)
548 {
549 }
550 CommandLineSwitchEnd(arg)
551 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
552
553 return CHANNEL_RC_OK;
554 }
555
556 #ifdef BUILTIN_CHANNELS
557 #define DVCPluginEntry tsmf_DVCPluginEntry
558 #else
559 #define DVCPluginEntry FREERDP_API DVCPluginEntry
560 #endif
561
562 /**
563 * Function description
564 *
565 * @return 0 on success, otherwise a Win32 error code
566 */
DVCPluginEntry(IDRDYNVC_ENTRY_POINTS * pEntryPoints)567 UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
568 {
569 UINT status = 0;
570 TSMF_PLUGIN* tsmf;
571 TsmfClientContext* context;
572 UINT error = CHANNEL_RC_NO_MEMORY;
573 tsmf = (TSMF_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "tsmf");
574
575 if (!tsmf)
576 {
577 tsmf = (TSMF_PLUGIN*)calloc(1, sizeof(TSMF_PLUGIN));
578
579 if (!tsmf)
580 {
581 WLog_ERR(TAG, "calloc failed!");
582 return CHANNEL_RC_NO_MEMORY;
583 }
584
585 tsmf->iface.Initialize = tsmf_plugin_initialize;
586 tsmf->iface.Connected = NULL;
587 tsmf->iface.Disconnected = NULL;
588 tsmf->iface.Terminated = tsmf_plugin_terminated;
589 tsmf->rdpcontext =
590 ((freerdp*)((rdpSettings*)pEntryPoints->GetRdpSettings(pEntryPoints))->instance)
591 ->context;
592 context = (TsmfClientContext*)calloc(1, sizeof(TsmfClientContext));
593
594 if (!context)
595 {
596 WLog_ERR(TAG, "calloc failed!");
597 goto error_context;
598 }
599
600 context->handle = (void*)tsmf;
601 tsmf->iface.pInterface = (void*)context;
602
603 if (!tsmf_media_init())
604 {
605 error = ERROR_INVALID_OPERATION;
606 goto error_init;
607 }
608
609 status = pEntryPoints->RegisterPlugin(pEntryPoints, "tsmf", (IWTSPlugin*)tsmf);
610 }
611
612 if (status == CHANNEL_RC_OK)
613 {
614 status =
615 tsmf_process_addin_args((IWTSPlugin*)tsmf, pEntryPoints->GetPluginData(pEntryPoints));
616 }
617
618 return status;
619 error_init:
620 free(context);
621 error_context:
622 free(tsmf);
623 return error;
624 }
625