22#include <freerdp/config.h>
25#include <winpr/stream.h>
26#include <winpr/cmdline.h>
28#include <freerdp/client/tsmf.h>
30#include "tsmf_types.h"
31#include "tsmf_constants.h"
32#include "tsmf_ifman.h"
33#include "tsmf_media.h"
37BOOL tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id)
44 DEBUG_TSMF(
"No callback reference - unable to send eos response!");
48 if (callback && callback->stream_id && callback->channel && callback->channel->Write)
50 wStream* s = Stream_New(
nullptr, 24);
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);
58 Stream_Write_UINT32(s, callback->stream_id);
59 Stream_Write_UINT32(s, TSMM_CLIENT_EVENT_ENDOFSTREAM);
60 Stream_Write_UINT32(s, 0);
61 const size_t pos = Stream_GetPosition(s);
62 DEBUG_TSMF(
"EOS response size %" PRIuz
"", pos);
63 WINPR_ASSERT(pos <= UINT32_MAX);
65 callback->channel->Write(callback->channel, (UINT32)pos, Stream_Buffer(s),
nullptr);
69 WLog_ERR(TAG,
"response error %" PRId32, WINPR_CXX_COMPAT_CAST(int32_t, status));
78BOOL tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id,
79 UINT64 duration, UINT32 data_size)
87 wStream* s = Stream_New(
nullptr, 32);
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);
95 Stream_Write_UINT32(s, callback->stream_id);
96 Stream_Write_UINT64(s, duration);
97 Stream_Write_UINT64(s, data_size);
99 const size_t pos = Stream_GetPosition(s);
100 DEBUG_TSMF(
"ACK response size %" PRIuz
"", pos);
102 if (!callback->channel || !callback->channel->Write)
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));
110 status = callback->channel->Write(
111 callback->channel, WINPR_ASSERTING_INT_CAST(uint32_t, pos), Stream_Buffer(s),
nullptr);
116 WLog_ERR(TAG,
"response error %" PRId32, WINPR_CXX_COMPAT_CAST(int32_t, status));
119 Stream_Free(s, TRUE);
120 return (status == 0);
128static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
132 UINT error = CHANNEL_RC_OK;
133 BOOL processed = FALSE;
135 UINT32 MessageId = 0;
136 UINT32 FunctionId = 0;
137 UINT32 InterfaceId = 0;
139 const size_t cbSize = Stream_GetRemainingLength(data);
142 if (!Stream_CheckAndLogRequiredLength(TAG, data, 12) || (cbSize > UINT32_MAX))
143 return ERROR_INVALID_DATA;
146 output = Stream_New(
nullptr, 256);
149 return ERROR_OUTOFMEMORY;
151 Stream_Seek(output, 8);
152 Stream_Read_UINT32(input, InterfaceId);
153 Stream_Read_UINT32(input, MessageId);
154 Stream_Read_UINT32(input, FunctionId);
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;
166 ifman.input_size = (UINT32)(cbSize - 12U);
167 ifman.output = output;
168 ifman.output_pending = FALSE;
169 ifman.output_interface_id = InterfaceId;
176 case TSMF_INTERFACE_CAPABILITIES | STREAM_ID_NONE:
179 case RIM_EXCHANGE_CAPABILITY_REQUEST:
180 error = tsmf_ifman_rim_exchange_capability_request(&ifman);
184 case RIMCALL_RELEASE:
185 case RIMCALL_QUERYINTERFACE:
194 case TSMF_INTERFACE_DEFAULT | STREAM_ID_PROXY:
197 case SET_CHANNEL_PARAMS:
198 if (!Stream_CheckAndLogRequiredLength(TAG, input, GUID_SIZE + 4))
200 error = ERROR_INVALID_DATA;
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;
212 case EXCHANGE_CAPABILITIES_REQ:
213 error = tsmf_ifman_exchange_capability_request(&ifman);
217 case CHECK_FORMAT_SUPPORT_REQ:
218 error = tsmf_ifman_check_format_support_request(&ifman);
222 case ON_NEW_PRESENTATION:
223 error = tsmf_ifman_on_new_presentation(&ifman);
229 tsmf_ifman_add_stream(&ifman, ((
TSMF_PLUGIN*)callback->plugin)->rdpcontext);
233 case SET_TOPOLOGY_REQ:
234 error = tsmf_ifman_set_topology_request(&ifman);
239 error = tsmf_ifman_remove_stream(&ifman);
243 case SET_SOURCE_VIDEO_RECT:
244 error = tsmf_ifman_set_source_video_rect(&ifman);
248 case SHUTDOWN_PRESENTATION_REQ:
249 error = tsmf_ifman_shutdown_presentation(&ifman);
253 case ON_STREAM_VOLUME:
254 error = tsmf_ifman_on_stream_volume(&ifman);
258 case ON_CHANNEL_VOLUME:
259 error = tsmf_ifman_on_channel_volume(&ifman);
263 case SET_VIDEO_WINDOW:
264 error = tsmf_ifman_set_video_window(&ifman);
268 case UPDATE_GEOMETRY_INFO:
269 error = tsmf_ifman_update_geometry_info(&ifman);
274 error = tsmf_ifman_set_allocator(&ifman);
279 error = tsmf_ifman_notify_preroll(&ifman);
284 error = tsmf_ifman_on_sample(&ifman);
289 error = tsmf_ifman_on_flush(&ifman);
293 case ON_END_OF_STREAM:
294 error = tsmf_ifman_on_end_of_stream(&ifman);
298 case ON_PLAYBACK_STARTED:
299 error = tsmf_ifman_on_playback_started(&ifman);
303 case ON_PLAYBACK_PAUSED:
304 error = tsmf_ifman_on_playback_paused(&ifman);
308 case ON_PLAYBACK_RESTARTED:
309 error = tsmf_ifman_on_playback_restarted(&ifman);
313 case ON_PLAYBACK_STOPPED:
314 error = tsmf_ifman_on_playback_stopped(&ifman);
318 case ON_PLAYBACK_RATE_CHANGED:
319 error = tsmf_ifman_on_playback_rate_changed(&ifman);
323 case RIMCALL_RELEASE:
324 case RIMCALL_QUERYINTERFACE:
338 ifman.input =
nullptr;
342 WLog_ERR(TAG,
"ifman data received processing error %" PRIu32
"", error);
349 case RIMCALL_RELEASE:
353 ifman.output_pending = 1;
356 case RIMCALL_QUERYINTERFACE:
368 "Unknown InterfaceId: 0x%08" PRIX32
" MessageId: 0x%08" PRIX32
369 " FunctionId: 0x%08" PRIX32
"\n",
370 InterfaceId, MessageId, FunctionId);
377 if (processed && !ifman.output_pending)
380 const size_t length = Stream_GetPosition(output);
381 if (length > UINT32_MAX)
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),
392 WLog_ERR(TAG,
"response error %" PRIu32
"", error);
397 Stream_Free(output, TRUE);
406static UINT tsmf_on_close(IWTSVirtualChannelCallback* pChannelCallback)
408 TSMF_STREAM* stream =
nullptr;
409 TSMF_PRESENTATION* presentation =
nullptr;
413 if (callback->stream_id)
415 presentation = tsmf_presentation_find_by_id(callback->presentation_id);
419 stream = tsmf_stream_find_by_id(presentation, callback->stream_id);
422 tsmf_stream_free(stream);
426 free(pChannelCallback);
427 return CHANNEL_RC_OK;
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)
447 return CHANNEL_RC_NO_MEMORY;
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;
464static UINT tsmf_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
471 if (!tsmf->listener_callback)
472 return CHANNEL_RC_NO_MEMORY;
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;
488static UINT tsmf_plugin_terminated(IWTSPlugin* pPlugin)
492 free(tsmf->listener_callback);
494 return CHANNEL_RC_OK;
502static UINT tsmf_process_addin_args(IWTSPlugin* pPlugin,
const ADDIN_ARGV* args)
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,
516 flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
517 status = CommandLineParseArgumentsA(args->argc, args->argv, tsmf_args, flags, tsmf,
nullptr,
521 return ERROR_INVALID_DATA;
527 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
530 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"sys")
532 tsmf->audio_name = _strdup(arg->Value);
534 if (!tsmf->audio_name)
535 return ERROR_OUTOFMEMORY;
537 CommandLineSwitchCase(arg,
"dev")
539 tsmf->audio_device = _strdup(arg->Value);
541 if (!tsmf->audio_device)
542 return ERROR_OUTOFMEMORY;
544 CommandLineSwitchCase(arg,
"decoder")
546 tsmf->decoder_name = _strdup(arg->Value);
548 if (!tsmf->decoder_name)
549 return ERROR_OUTOFMEMORY;
551 CommandLineSwitchDefault(arg)
554 CommandLineSwitchEnd(arg)
555 }
while ((arg = CommandLineFindNextArgumentA(arg)) !=
nullptr);
557 return CHANNEL_RC_OK;
565FREERDP_ENTRY_POINT(UINT VCAPITYPE tsmf_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
569 TsmfClientContext* context =
nullptr;
570 UINT error = CHANNEL_RC_NO_MEMORY;
571 tsmf = (
TSMF_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints,
"tsmf");
579 WLog_ERR(TAG,
"calloc failed!");
580 return CHANNEL_RC_NO_MEMORY;
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));
592 WLog_ERR(TAG,
"calloc failed!");
596 context->handle = (
void*)tsmf;
597 tsmf->iface.pInterface = (
void*)context;
599 if (!tsmf_media_init())
601 error = ERROR_INVALID_OPERATION;
605 status = pEntryPoints->RegisterPlugin(pEntryPoints,
"tsmf", &tsmf->iface);
608 if (status == CHANNEL_RC_OK)
610 status = tsmf_process_addin_args(&tsmf->iface, pEntryPoints->GetPluginData(pEntryPoints));