20#include <winpr/assert.h>
21#include <winpr/cast.h>
22#include <winpr/string.h>
26#define TAG CHANNELS_TAG("rdpecam-enum.client")
36 CAM_MSG_ID msg = CAM_MSG_ID_ErrorResponse;
40 wStream* s = Stream_New(
nullptr, CAM_HEADER_SIZE + 4);
43 WLog_ERR(TAG,
"Stream_New failed!");
44 return ERROR_NOT_ENOUGH_MEMORY;
47 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
48 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
49 Stream_Write_UINT32(s, code);
51 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
64 wStream* s = Stream_New(
nullptr, CAM_HEADER_SIZE);
67 WLog_ERR(TAG,
"Stream_New failed!");
68 return ERROR_NOT_ENOUGH_MEMORY;
71 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
72 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
74 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
83 CAM_MSG_ID msg,
wStream* out, BOOL freeStream)
85 if (!hchannel || !out)
86 return ERROR_INVALID_PARAMETER;
88 Stream_SealLength(out);
90 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Length(out));
92 WLog_DBG(TAG,
"ChannelId=%" PRIu32
", MessageId=0x%02" PRIx8
", Length=%" PRIu32,
93 hchannel->channel_mgr->GetChannelId(hchannel->channel), msg, len);
96 hchannel->channel->Write(hchannel->channel, len, Stream_Buffer(out),
nullptr);
99 Stream_Free(out, TRUE);
109static UINT ecam_send_device_added_notification(
CameraPlugin* ecam,
111 const char* deviceName,
const char* channelName)
113 CAM_MSG_ID msg = CAM_MSG_ID_DeviceAddedNotification;
117 wStream* s = Stream_New(
nullptr, 256);
120 WLog_ERR(TAG,
"Stream_New failed!");
121 return ERROR_NOT_ENOUGH_MEMORY;
124 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
125 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
127 const size_t devNameLen = strlen(deviceName);
128 const SSIZE_T devNameWLen = ConvertUtf8ToWChar(deviceName,
nullptr, 0);
129 if ((devNameWLen < 0) ||
130 Stream_Write_UTF16_String_From_UTF8(s, WINPR_ASSERTING_INT_CAST(
size_t, devNameWLen) + 1ull,
131 deviceName, devNameLen, TRUE) < 0)
133 Stream_Free(s, TRUE);
134 return ERROR_INTERNAL_ERROR;
136 Stream_Write(s, channelName, strlen(channelName) + 1);
138 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
147 const char* deviceId,
const char* deviceName)
149 WLog_DBG(TAG,
"deviceId=%s, deviceName=%s", deviceId, deviceName);
151 if (!HashTable_ContainsKey(ecam->devices, deviceId))
153 CameraDevice* dev = ecam_dev_create(ecam, deviceId, deviceName);
154 if (!HashTable_Insert(ecam->devices, deviceId, dev))
156 ecam_dev_destroy(dev);
157 return ERROR_INTERNAL_ERROR;
162 WLog_DBG(TAG,
"Device %s already exists", deviceId);
165 ecam_send_device_added_notification(ecam, hchannel, deviceName, deviceId );
167 return CHANNEL_RC_OK;
177 return ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
185static UINT ecam_process_select_version_response(
CameraPlugin* ecam,
187 WINPR_ATTR_UNUSED
wStream* s, BYTE serverVersion)
189 const BYTE clientVersion = ECAM_PROTO_VERSION;
193 WLog_DBG(TAG,
"ServerVersion=%" PRIu8
", ClientVersion=%" PRIu8, serverVersion, clientVersion);
195 if (serverVersion > clientVersion)
198 "Incompatible protocol version server=%" PRIu8
", client supports version=%" PRIu8,
199 serverVersion, clientVersion);
200 return CHANNEL_RC_OK;
202 ecam->version = serverVersion;
205 ecam_enumerate_devices(ecam, hchannel);
207 WLog_ERR(TAG,
"No HAL registered");
209 return CHANNEL_RC_OK;
217static UINT ecam_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
219 UINT error = CHANNEL_RC_OK;
224 if (!hchannel || !data)
225 return ERROR_INVALID_PARAMETER;
230 return ERROR_INTERNAL_ERROR;
232 if (!Stream_CheckAndLogRequiredLength(TAG, data, CAM_HEADER_SIZE))
233 return ERROR_NO_DATA;
235 Stream_Read_UINT8(data, version);
236 Stream_Read_UINT8(data, messageId);
237 WLog_DBG(TAG,
"ChannelId=%" PRIu32
", MessageId=0x%02" PRIx8
", Version=%d",
238 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
242 case CAM_MSG_ID_SelectVersionResponse:
243 error = ecam_process_select_version_response(ecam, hchannel, data, version);
247 WLog_WARN(TAG,
"unknown MessageId=0x%02" PRIx8
"", messageId);
248 error = ERROR_INVALID_DATA;
249 ecam_channel_send_error_response(ecam, hchannel, CAM_ERROR_CODE_OperationNotSupported);
261static UINT ecam_on_open(IWTSVirtualChannelCallback* pChannelCallback)
264 WINPR_ASSERT(hchannel);
269 WLog_DBG(TAG,
"entered");
270 return ecam_channel_send_generic_msg(ecam, hchannel, CAM_MSG_ID_SelectVersionRequest);
278static UINT ecam_on_close(IWTSVirtualChannelCallback* pChannelCallback)
281 WINPR_ASSERT(hchannel);
283 WLog_DBG(TAG,
"entered");
286 return CHANNEL_RC_OK;
294static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
295 IWTSVirtualChannel* pChannel,
296 WINPR_ATTR_UNUSED BYTE* Data,
297 WINPR_ATTR_UNUSED BOOL* pbAccept,
298 IWTSVirtualChannelCallback** ppCallback)
302 if (!hlistener || !hlistener->plugin)
303 return ERROR_INTERNAL_ERROR;
305 WLog_DBG(TAG,
"entered");
311 WLog_ERR(TAG,
"calloc failed!");
312 return CHANNEL_RC_NO_MEMORY;
315 hchannel->iface.OnDataReceived = ecam_on_data_received;
316 hchannel->iface.OnOpen = ecam_on_open;
317 hchannel->iface.OnClose = ecam_on_close;
318 hchannel->plugin = hlistener->plugin;
319 hchannel->channel_mgr = hlistener->channel_mgr;
320 hchannel->channel = pChannel;
321 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
322 return CHANNEL_RC_OK;
325static void ecam_dev_destroy_pv(
void* obj)
328 ecam_dev_destroy(dev);
336static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
340 WLog_DBG(TAG,
"entered");
342 if (!ecam || !pChannelMgr)
343 return ERROR_INVALID_PARAMETER;
345 if (ecam->initialized)
347 WLog_ERR(TAG,
"[%s] plugin initialized twice, aborting", RDPECAM_CONTROL_DVC_CHANNEL_NAME);
348 return ERROR_INVALID_DATA;
351 ecam->version = ECAM_PROTO_VERSION;
353 ecam->devices = HashTable_New(FALSE);
356 WLog_ERR(TAG,
"HashTable_New failed!");
357 return CHANNEL_RC_NO_MEMORY;
360 if (!HashTable_SetupForStringData(ecam->devices, FALSE))
362 HashTable_Free(ecam->devices);
363 ecam->devices =
nullptr;
364 return ERROR_INTERNAL_ERROR;
367 wObject* obj = HashTable_ValueObject(ecam->devices);
373 if (!ecam->hlistener)
375 WLog_ERR(TAG,
"calloc failed!");
376 return CHANNEL_RC_NO_MEMORY;
379 ecam->hlistener->iface.OnNewChannelConnection = ecam_on_new_channel_connection;
380 ecam->hlistener->plugin = pPlugin;
381 ecam->hlistener->channel_mgr = pChannelMgr;
382 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, RDPECAM_CONTROL_DVC_CHANNEL_NAME, 0,
383 &ecam->hlistener->iface, &ecam->listener);
385 ecam->initialized = (rc == CHANNEL_RC_OK);
394static UINT ecam_plugin_terminated(IWTSPlugin* pPlugin)
399 return ERROR_INVALID_DATA;
401 WLog_DBG(TAG,
"entered");
405 IWTSVirtualChannelManager* mgr = ecam->hlistener->channel_mgr;
407 IFCALL(mgr->DestroyListener, mgr, ecam->listener);
410 free(ecam->hlistener);
412 HashTable_Free(ecam->devices);
414 UINT rc = CHANNEL_RC_OK;
416 rc = ecam->ihal->Free(ecam->ihal);
427static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
430 UINT error = CHANNEL_RC_OK;
433 return ERROR_INVALID_PARAMETER;
435 ecam->attached = TRUE;
444static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
447 UINT error = CHANNEL_RC_OK;
450 return ERROR_INVALID_PARAMETER;
452 ecam->attached = FALSE;
461static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
469 WLog_DBG(TAG,
"already registered");
470 return ERROR_ALREADY_EXISTS;
473 WLog_DBG(TAG,
"HAL registered");
475 return CHANNEL_RC_OK;
488 UINT error = ERROR_INTERNAL_ERROR;
491 PVIRTUALCHANNELENTRY pvce;
492 const PFREERDP_CAMERA_HAL_ENTRY entry;
494 cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name,
nullptr, 0);
496 if (cnv.entry ==
nullptr)
499 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
501 return ERROR_INVALID_FUNCTION;
504 entryPoints.plugin = &ecam->iface;
505 entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
506 entryPoints.args = args;
507 entryPoints.ecam = ecam;
509 error = cnv.entry(&entryPoints);
512 WLog_ERR(TAG,
"%s entry returned error %" PRIu32
".", name, error);
516 WLog_INFO(TAG,
"Loaded %s HAL for ecam", name);
517 return CHANNEL_RC_OK;
525FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
527 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
529 WINPR_ASSERT(pEntryPoints);
530 WINPR_ASSERT(pEntryPoints->GetPlugin);
534 return CHANNEL_RC_ALREADY_INITIALIZED;
540 WLog_ERR(TAG,
"calloc failed!");
541 return CHANNEL_RC_NO_MEMORY;
544 ecam->attached = TRUE;
545 ecam->iface.Initialize = ecam_plugin_initialize;
546 ecam->iface.Connected =
nullptr;
547 ecam->iface.Disconnected =
nullptr;
548 ecam->iface.Terminated = ecam_plugin_terminated;
549 ecam->iface.Attached = ecam_plugin_attached;
550 ecam->iface.Detached = ecam_plugin_detached;
554 ecam->subsystem =
"v4l";
555#elif defined(__ANDROID__)
556 ecam->subsystem =
"android";
558 ecam->subsystem =
nullptr;
563 if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem,
nullptr )))
566 "Unable to load camera redirection subsystem %s because of error %" PRIu32
"",
567 ecam->subsystem, error);
572 error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
573 if (error == CHANNEL_RC_OK)
577 ecam_plugin_terminated(&ecam->iface);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree