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