20#include <winpr/assert.h>
21#include <winpr/cast.h>
22#include <winpr/cmdline.h>
23#include <winpr/string.h>
27#define TAG CHANNELS_TAG("rdpecam-enum.client")
37 CAM_MSG_ID msg = CAM_MSG_ID_ErrorResponse;
41 wStream* s = Stream_New(
nullptr, CAM_HEADER_SIZE + 4);
44 WLog_ERR(TAG,
"Stream_New failed!");
45 return ERROR_NOT_ENOUGH_MEMORY;
48 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
49 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
50 Stream_Write_UINT32(s, code);
52 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
65 wStream* s = Stream_New(
nullptr, CAM_HEADER_SIZE);
68 WLog_ERR(TAG,
"Stream_New failed!");
69 return ERROR_NOT_ENOUGH_MEMORY;
72 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
73 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
75 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
84 CAM_MSG_ID msg,
wStream* out, BOOL freeStream)
86 if (!hchannel || !out)
87 return ERROR_INVALID_PARAMETER;
89 Stream_SealLength(out);
91 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Length(out));
93 WLog_DBG(TAG,
"ChannelId=%" PRIu32
", MessageId=0x%02" PRIx8
", Length=%" PRIu32,
94 hchannel->channel_mgr->GetChannelId(hchannel->channel), msg, len);
97 hchannel->channel->Write(hchannel->channel, len, Stream_Buffer(out),
nullptr);
100 Stream_Free(out, TRUE);
110static UINT ecam_send_device_added_notification(
CameraPlugin* ecam,
112 const char* deviceName,
const char* channelName)
114 CAM_MSG_ID msg = CAM_MSG_ID_DeviceAddedNotification;
118 wStream* s = Stream_New(
nullptr, 256);
121 WLog_ERR(TAG,
"Stream_New failed!");
122 return ERROR_NOT_ENOUGH_MEMORY;
125 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
126 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
128 const size_t devNameLen = strlen(deviceName);
129 const SSIZE_T devNameWLen = ConvertUtf8ToWChar(deviceName,
nullptr, 0);
130 if ((devNameWLen < 0) ||
131 Stream_Write_UTF16_String_From_UTF8(s, WINPR_ASSERTING_INT_CAST(
size_t, devNameWLen) + 1ull,
132 deviceName, devNameLen, TRUE) < 0)
134 Stream_Free(s, TRUE);
135 return ERROR_INTERNAL_ERROR;
137 Stream_Write(s, channelName, strlen(channelName) + 1);
139 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
148 const char* deviceId,
const char* deviceName)
150 WLog_DBG(TAG,
"deviceId=%s, deviceName=%s", deviceId, deviceName);
152 if (!HashTable_ContainsKey(ecam->devices, deviceId))
154 CameraDevice* dev = ecam_dev_create(ecam, deviceId, deviceName);
155 if (!HashTable_Insert(ecam->devices, deviceId, dev))
157 ecam_dev_destroy(dev);
158 return ERROR_INTERNAL_ERROR;
163 WLog_DBG(TAG,
"Device %s already exists", deviceId);
166 ecam_send_device_added_notification(ecam, hchannel, deviceName, deviceId );
168 return CHANNEL_RC_OK;
178 return ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
186static UINT ecam_process_select_version_response(
CameraPlugin* ecam,
188 WINPR_ATTR_UNUSED
wStream* s, BYTE serverVersion)
190 const BYTE clientVersion = ECAM_PROTO_VERSION;
194 WLog_DBG(TAG,
"ServerVersion=%" PRIu8
", ClientVersion=%" PRIu8, serverVersion, clientVersion);
196 if (serverVersion > clientVersion)
199 "Incompatible protocol version server=%" PRIu8
", client supports version=%" PRIu8,
200 serverVersion, clientVersion);
201 return CHANNEL_RC_OK;
203 ecam->version = serverVersion;
206 ecam_enumerate_devices(ecam, hchannel);
208 WLog_ERR(TAG,
"No HAL registered");
210 return CHANNEL_RC_OK;
218static UINT ecam_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
220 UINT error = CHANNEL_RC_OK;
225 if (!hchannel || !data)
226 return ERROR_INVALID_PARAMETER;
231 return ERROR_INTERNAL_ERROR;
233 if (!Stream_CheckAndLogRequiredLength(TAG, data, CAM_HEADER_SIZE))
234 return ERROR_NO_DATA;
236 Stream_Read_UINT8(data, version);
237 Stream_Read_UINT8(data, messageId);
238 WLog_DBG(TAG,
"ChannelId=%" PRIu32
", MessageId=0x%02" PRIx8
", Version=%d",
239 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
243 case CAM_MSG_ID_SelectVersionResponse:
244 error = ecam_process_select_version_response(ecam, hchannel, data, version);
248 WLog_WARN(TAG,
"unknown MessageId=0x%02" PRIx8
"", messageId);
249 error = ERROR_INVALID_DATA;
250 ecam_channel_send_error_response(ecam, hchannel, CAM_ERROR_CODE_OperationNotSupported);
262static UINT ecam_on_open(IWTSVirtualChannelCallback* pChannelCallback)
265 WINPR_ASSERT(hchannel);
270 WLog_DBG(TAG,
"entered");
271 return ecam_channel_send_generic_msg(ecam, hchannel, CAM_MSG_ID_SelectVersionRequest);
279static UINT ecam_on_close(IWTSVirtualChannelCallback* pChannelCallback)
282 WINPR_ASSERT(hchannel);
284 WLog_DBG(TAG,
"entered");
288 ecam->connected = FALSE;
291 return CHANNEL_RC_OK;
299static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
300 IWTSVirtualChannel* pChannel,
301 WINPR_ATTR_UNUSED BYTE* Data,
302 WINPR_ATTR_UNUSED BOOL* pbAccept,
303 IWTSVirtualChannelCallback** ppCallback)
307 if (!hlistener || !hlistener->plugin)
308 return ERROR_INTERNAL_ERROR;
310 WLog_DBG(TAG,
"entered");
317 WLog_ERR(TAG,
"Channel already connected, terminating.");
318 return ERROR_DEVICE_ALREADY_ATTACHED;
326 WLog_ERR(TAG,
"calloc failed!");
327 return CHANNEL_RC_NO_MEMORY;
330 hchannel->iface.OnDataReceived = ecam_on_data_received;
331 hchannel->iface.OnOpen = ecam_on_open;
332 hchannel->iface.OnClose = ecam_on_close;
333 hchannel->plugin = hlistener->plugin;
334 hchannel->channel_mgr = hlistener->channel_mgr;
335 hchannel->channel = pChannel;
336 *ppCallback = &hchannel->iface;
337 ecam->connected = TRUE;
338 return CHANNEL_RC_OK;
341static void ecam_dev_destroy_pv(
void* obj)
344 ecam_dev_destroy(dev);
352static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
356 WLog_DBG(TAG,
"entered");
358 if (!ecam || !pChannelMgr)
359 return ERROR_INVALID_PARAMETER;
361 if (ecam->initialized)
363 WLog_ERR(TAG,
"[%s] plugin initialized twice, aborting", RDPECAM_CONTROL_DVC_CHANNEL_NAME);
364 return ERROR_INVALID_DATA;
367 ecam->version = ECAM_PROTO_VERSION;
369 ecam->devices = HashTable_New(FALSE);
372 WLog_ERR(TAG,
"HashTable_New failed!");
373 return CHANNEL_RC_NO_MEMORY;
376 if (!HashTable_SetupForStringData(ecam->devices, FALSE))
378 HashTable_Free(ecam->devices);
379 ecam->devices =
nullptr;
380 return ERROR_INTERNAL_ERROR;
383 wObject* obj = HashTable_ValueObject(ecam->devices);
389 if (!ecam->hlistener)
391 WLog_ERR(TAG,
"calloc failed!");
392 return CHANNEL_RC_NO_MEMORY;
395 ecam->hlistener->iface.OnNewChannelConnection = ecam_on_new_channel_connection;
396 ecam->hlistener->plugin = pPlugin;
397 ecam->hlistener->channel_mgr = pChannelMgr;
398 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, RDPECAM_CONTROL_DVC_CHANNEL_NAME, 0,
399 &ecam->hlistener->iface, &ecam->listener);
401 ecam->initialized = (rc == CHANNEL_RC_OK);
410static UINT ecam_plugin_terminated(IWTSPlugin* pPlugin)
415 return ERROR_INVALID_DATA;
417 WLog_DBG(TAG,
"entered");
421 IWTSVirtualChannelManager* mgr = ecam->hlistener->channel_mgr;
423 IFCALL(mgr->DestroyListener, mgr, ecam->listener);
426 free(ecam->hlistener);
428 HashTable_Free(ecam->devices);
430 UINT rc = CHANNEL_RC_OK;
432 rc = ecam->ihal->Free(ecam->ihal);
443static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
446 UINT error = CHANNEL_RC_OK;
449 return ERROR_INVALID_PARAMETER;
451 ecam->attached = TRUE;
460static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
463 UINT error = CHANNEL_RC_OK;
466 return ERROR_INVALID_PARAMETER;
468 ecam->attached = FALSE;
477static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
485 WLog_DBG(TAG,
"already registered");
486 return ERROR_ALREADY_EXISTS;
489 WLog_DBG(TAG,
"HAL registered");
491 return CHANNEL_RC_OK;
504 UINT error = ERROR_INTERNAL_ERROR;
507 PVIRTUALCHANNELENTRY pvce;
508 const PFREERDP_CAMERA_HAL_ENTRY entry;
510 cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name,
nullptr, 0);
512 if (cnv.entry ==
nullptr)
515 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
517 return ERROR_INVALID_FUNCTION;
520 entryPoints.plugin = &ecam->iface;
521 entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
522 entryPoints.args = args;
523 entryPoints.ecam = ecam;
525 error = cnv.entry(&entryPoints);
528 WLog_ERR(TAG,
"%s entry returned error %" PRIu32
".", name, error);
532 WLog_INFO(TAG,
"Loaded %s HAL for ecam", name);
533 return CHANNEL_RC_OK;
549 nullptr,
nullptr, -1,
nullptr,
"subsystem" },
550 {
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
553 if (!args || (args->argc <= 1))
557 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
558 const int status = CommandLineParseArgumentsA(args->argc, args->argv, ecam_args, flags, ecam,
564 if (arg && (arg->Flags & COMMAND_LINE_VALUE_PRESENT))
567 ecam->subsystem = arg->Value;
577FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
579 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
581 WINPR_ASSERT(pEntryPoints);
582 WINPR_ASSERT(pEntryPoints->GetPlugin);
586 return CHANNEL_RC_ALREADY_INITIALIZED;
592 WLog_ERR(TAG,
"calloc failed!");
593 return CHANNEL_RC_NO_MEMORY;
596 ecam->attached = TRUE;
597 ecam->iface.Initialize = ecam_plugin_initialize;
598 ecam->iface.Connected =
nullptr;
599 ecam->iface.Disconnected =
nullptr;
600 ecam->iface.Terminated = ecam_plugin_terminated;
601 ecam->iface.Attached = ecam_plugin_attached;
602 ecam->iface.Detached = ecam_plugin_detached;
606 ecam->subsystem =
"v4l";
607#elif defined(__ANDROID__)
608 ecam->subsystem =
"android";
609#elif defined(__APPLE__)
610 ecam->subsystem =
"avfoundation";
612 ecam->subsystem =
nullptr;
615 const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
616 if (!ecam_process_addin_args(ecam, args))
618 WLog_ERR(TAG,
"Invalid rdpecam arguments");
619 error = ERROR_INVALID_PARAMETER;
625 if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem, args)))
628 "Unable to load camera redirection subsystem %s because of error %" PRIu32
"",
629 ecam->subsystem, error);
634 error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
635 if (error == CHANNEL_RC_OK)
639 ecam_plugin_terminated(&ecam->iface);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree