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