FreeRDP
Loading...
Searching...
No Matches
camera_device_enum_main.c
1
20#include <winpr/assert.h>
21#include <winpr/cast.h>
22
23#include "camera.h"
24
25#define TAG CHANNELS_TAG("rdpecam-enum.client")
26
32UINT ecam_channel_send_error_response(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
33 CAM_ERROR_CODE code)
34{
35 CAM_MSG_ID msg = CAM_MSG_ID_ErrorResponse;
36
37 WINPR_ASSERT(ecam);
38
39 wStream* s = Stream_New(NULL, CAM_HEADER_SIZE + 4);
40 if (!s)
41 {
42 WLog_ERR(TAG, "Stream_New failed!");
43 return ERROR_NOT_ENOUGH_MEMORY;
44 }
45
46 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
47 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
48 Stream_Write_UINT32(s, code);
49
50 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
51}
52
58UINT ecam_channel_send_generic_msg(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
59 CAM_MSG_ID msg)
60{
61 WINPR_ASSERT(ecam);
62
63 wStream* s = Stream_New(NULL, CAM_HEADER_SIZE);
64 if (!s)
65 {
66 WLog_ERR(TAG, "Stream_New failed!");
67 return ERROR_NOT_ENOUGH_MEMORY;
68 }
69
70 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
71 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
72
73 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
74}
75
81UINT ecam_channel_write(WINPR_ATTR_UNUSED CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
82 CAM_MSG_ID msg, wStream* out, BOOL freeStream)
83{
84 if (!hchannel || !out)
85 return ERROR_INVALID_PARAMETER;
86
87 Stream_SealLength(out);
88 WINPR_ASSERT(Stream_Length(out) <= UINT32_MAX);
89
90 WLog_DBG(TAG, "ChannelId=%" PRIu32 ", MessageId=0x%02" PRIx8 ", Length=%" PRIuz,
91 hchannel->channel_mgr->GetChannelId(hchannel->channel), msg, Stream_Length(out));
92
93 const UINT error = hchannel->channel->Write(hchannel->channel, (ULONG)Stream_Length(out),
94 Stream_Buffer(out), NULL);
95
96 if (freeStream)
97 Stream_Free(out, TRUE);
98
99 return error;
100}
101
107static UINT ecam_send_device_added_notification(CameraPlugin* ecam,
108 GENERIC_CHANNEL_CALLBACK* hchannel,
109 const char* deviceName, const char* channelName)
110{
111 CAM_MSG_ID msg = CAM_MSG_ID_DeviceAddedNotification;
112
113 WINPR_ASSERT(ecam);
114
115 wStream* s = Stream_New(NULL, 256);
116 if (!s)
117 {
118 WLog_ERR(TAG, "Stream_New failed!");
119 return ERROR_NOT_ENOUGH_MEMORY;
120 }
121
122 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
123 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
124
125 size_t devNameLen = strlen(deviceName);
126 if (Stream_Write_UTF16_String_From_UTF8(s, devNameLen + 1, deviceName, devNameLen, TRUE) < 0)
127 {
128 Stream_Free(s, TRUE);
129 return ERROR_INTERNAL_ERROR;
130 }
131 Stream_Write(s, channelName, strlen(channelName) + 1);
132
133 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
134}
135
141static UINT ecam_ihal_device_added_callback(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
142 const char* deviceId, const char* deviceName)
143{
144 WLog_DBG(TAG, "deviceId=%s, deviceName=%s", deviceId, deviceName);
145
146 if (!HashTable_ContainsKey(ecam->devices, deviceId))
147 {
148 CameraDevice* dev = ecam_dev_create(ecam, deviceId, deviceName);
149 if (!HashTable_Insert(ecam->devices, deviceId, dev))
150 {
151 ecam_dev_destroy(dev);
152 return ERROR_INTERNAL_ERROR;
153 }
154 }
155 else
156 {
157 WLog_DBG(TAG, "Device %s already exists", deviceId);
158 }
159
160 ecam_send_device_added_notification(ecam, hchannel, deviceName, deviceId /*channelName*/);
161
162 return CHANNEL_RC_OK;
163}
164
170static UINT ecam_enumerate_devices(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
171{
172 return ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
173}
174
180static UINT ecam_process_select_version_response(CameraPlugin* ecam,
181 GENERIC_CHANNEL_CALLBACK* hchannel,
182 WINPR_ATTR_UNUSED wStream* s, BYTE serverVersion)
183{
184 const BYTE clientVersion = ECAM_PROTO_VERSION;
185
186 /* check remaining s capacity */
187
188 WLog_DBG(TAG, "ServerVersion=%" PRIu8 ", ClientVersion=%" PRIu8, serverVersion, clientVersion);
189
190 if (serverVersion > clientVersion)
191 {
192 WLog_ERR(TAG,
193 "Incompatible protocol version server=%" PRIu8 ", client supports version=%" PRIu8,
194 serverVersion, clientVersion);
195 return CHANNEL_RC_OK;
196 }
197 ecam->version = serverVersion;
198
199 if (ecam->ihal)
200 ecam_enumerate_devices(ecam, hchannel);
201 else
202 WLog_ERR(TAG, "No HAL registered");
203
204 return CHANNEL_RC_OK;
205}
206
212static UINT ecam_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
213{
214 UINT error = CHANNEL_RC_OK;
215 BYTE version = 0;
216 BYTE messageId = 0;
217 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
218
219 if (!hchannel || !data)
220 return ERROR_INVALID_PARAMETER;
221
222 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
223
224 if (!ecam)
225 return ERROR_INTERNAL_ERROR;
226
227 if (!Stream_CheckAndLogRequiredCapacity(TAG, data, CAM_HEADER_SIZE))
228 return ERROR_NO_DATA;
229
230 Stream_Read_UINT8(data, version);
231 Stream_Read_UINT8(data, messageId);
232 WLog_DBG(TAG, "ChannelId=%" PRIu32 ", MessageId=0x%02" PRIx8 ", Version=%d",
233 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
234
235 switch (messageId)
236 {
237 case CAM_MSG_ID_SelectVersionResponse:
238 error = ecam_process_select_version_response(ecam, hchannel, data, version);
239 break;
240
241 default:
242 WLog_WARN(TAG, "unknown MessageId=0x%02" PRIx8 "", messageId);
243 error = ERROR_INVALID_DATA;
244 ecam_channel_send_error_response(ecam, hchannel, CAM_ERROR_CODE_OperationNotSupported);
245 break;
246 }
247
248 return error;
249}
250
256static UINT ecam_on_open(IWTSVirtualChannelCallback* pChannelCallback)
257{
258 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
259 WINPR_ASSERT(hchannel);
260
261 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
262 WINPR_ASSERT(ecam);
263
264 WLog_DBG(TAG, "entered");
265 return ecam_channel_send_generic_msg(ecam, hchannel, CAM_MSG_ID_SelectVersionRequest);
266}
267
273static UINT ecam_on_close(IWTSVirtualChannelCallback* pChannelCallback)
274{
275 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
276 WINPR_ASSERT(hchannel);
277
278 WLog_DBG(TAG, "entered");
279
280 free(hchannel);
281 return CHANNEL_RC_OK;
282}
283
289static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
290 IWTSVirtualChannel* pChannel,
291 WINPR_ATTR_UNUSED BYTE* Data,
292 WINPR_ATTR_UNUSED BOOL* pbAccept,
293 IWTSVirtualChannelCallback** ppCallback)
294{
295 GENERIC_LISTENER_CALLBACK* hlistener = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
296
297 if (!hlistener || !hlistener->plugin)
298 return ERROR_INTERNAL_ERROR;
299
300 WLog_DBG(TAG, "entered");
301 GENERIC_CHANNEL_CALLBACK* hchannel =
303
304 if (!hchannel)
305 {
306 WLog_ERR(TAG, "calloc failed!");
307 return CHANNEL_RC_NO_MEMORY;
308 }
309
310 hchannel->iface.OnDataReceived = ecam_on_data_received;
311 hchannel->iface.OnOpen = ecam_on_open;
312 hchannel->iface.OnClose = ecam_on_close;
313 hchannel->plugin = hlistener->plugin;
314 hchannel->channel_mgr = hlistener->channel_mgr;
315 hchannel->channel = pChannel;
316 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
317 return CHANNEL_RC_OK;
318}
319
320static void ecam_dev_destroy_pv(void* obj)
321{
322 CameraDevice* dev = obj;
323 ecam_dev_destroy(dev);
324}
325
331static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
332{
333 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
334
335 WLog_DBG(TAG, "entered");
336
337 if (!ecam || !pChannelMgr)
338 return ERROR_INVALID_PARAMETER;
339
340 if (ecam->initialized)
341 {
342 WLog_ERR(TAG, "[%s] plugin initialized twice, aborting", RDPECAM_CONTROL_DVC_CHANNEL_NAME);
343 return ERROR_INVALID_DATA;
344 }
345
346 ecam->version = ECAM_PROTO_VERSION;
347
348 ecam->devices = HashTable_New(FALSE);
349 if (!ecam->devices)
350 {
351 WLog_ERR(TAG, "HashTable_New failed!");
352 return CHANNEL_RC_NO_MEMORY;
353 }
354
355 HashTable_SetupForStringData(ecam->devices, FALSE);
356
357 wObject* obj = HashTable_ValueObject(ecam->devices);
358 WINPR_ASSERT(obj);
359 obj->fnObjectFree = ecam_dev_destroy_pv;
360
361 ecam->hlistener = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
362
363 if (!ecam->hlistener)
364 {
365 WLog_ERR(TAG, "calloc failed!");
366 return CHANNEL_RC_NO_MEMORY;
367 }
368
369 ecam->hlistener->iface.OnNewChannelConnection = ecam_on_new_channel_connection;
370 ecam->hlistener->plugin = pPlugin;
371 ecam->hlistener->channel_mgr = pChannelMgr;
372 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, RDPECAM_CONTROL_DVC_CHANNEL_NAME, 0,
373 &ecam->hlistener->iface, &ecam->listener);
374
375 ecam->initialized = (rc == CHANNEL_RC_OK);
376 return rc;
377}
378
384static UINT ecam_plugin_terminated(IWTSPlugin* pPlugin)
385{
386 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
387
388 if (!ecam)
389 return ERROR_INVALID_DATA;
390
391 WLog_DBG(TAG, "entered");
392
393 if (ecam->hlistener)
394 {
395 IWTSVirtualChannelManager* mgr = ecam->hlistener->channel_mgr;
396 if (mgr)
397 IFCALL(mgr->DestroyListener, mgr, ecam->listener);
398 }
399
400 free(ecam->hlistener);
401
402 HashTable_Free(ecam->devices);
403
404 UINT rc = CHANNEL_RC_OK;
405 if (ecam->ihal)
406 rc = ecam->ihal->Free(ecam->ihal);
407
408 free(ecam);
409 return rc;
410}
411
417static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
418{
419 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
420 UINT error = CHANNEL_RC_OK;
421
422 if (!ecam)
423 return ERROR_INVALID_PARAMETER;
424
425 ecam->attached = TRUE;
426 return error;
427}
428
434static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
435{
436 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
437 UINT error = CHANNEL_RC_OK;
438
439 if (!ecam)
440 return ERROR_INVALID_PARAMETER;
441
442 ecam->attached = FALSE;
443 return error;
444}
445
451static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
452{
453 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
454
455 WINPR_ASSERT(ecam);
456
457 if (ecam->ihal)
458 {
459 WLog_DBG(TAG, "already registered");
460 return ERROR_ALREADY_EXISTS;
461 }
462
463 WLog_DBG(TAG, "HAL registered");
464 ecam->ihal = ihal;
465 return CHANNEL_RC_OK;
466}
467
473static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADDIN_ARGV* args)
474{
475 WINPR_ASSERT(ecam);
476
477 FREERDP_CAMERA_HAL_ENTRY_POINTS entryPoints = { 0 };
478 UINT error = ERROR_INTERNAL_ERROR;
479 union
480 {
481 PVIRTUALCHANNELENTRY pvce;
482 const PFREERDP_CAMERA_HAL_ENTRY entry;
483 } cnv;
484 cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name, NULL, 0);
485
486 if (cnv.entry == NULL)
487 {
488 WLog_ERR(TAG,
489 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
490 name);
491 return ERROR_INVALID_FUNCTION;
492 }
493
494 entryPoints.plugin = &ecam->iface;
495 entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
496 entryPoints.args = args;
497 entryPoints.ecam = ecam;
498
499 error = cnv.entry(&entryPoints);
500 if (error)
501 {
502 WLog_ERR(TAG, "%s entry returned error %" PRIu32 ".", name, error);
503 return error;
504 }
505
506 WLog_INFO(TAG, "Loaded %s HAL for ecam", name);
507 return CHANNEL_RC_OK;
508}
509
515FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
516{
517 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
518
519 WINPR_ASSERT(pEntryPoints);
520 WINPR_ASSERT(pEntryPoints->GetPlugin);
521 CameraPlugin* ecam = (CameraPlugin*)pEntryPoints->GetPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME);
522
523 if (ecam != NULL)
524 return CHANNEL_RC_ALREADY_INITIALIZED;
525
526 ecam = (CameraPlugin*)calloc(1, sizeof(CameraPlugin));
527
528 if (!ecam)
529 {
530 WLog_ERR(TAG, "calloc failed!");
531 return CHANNEL_RC_NO_MEMORY;
532 }
533
534 ecam->attached = TRUE;
535 ecam->iface.Initialize = ecam_plugin_initialize;
536 ecam->iface.Connected = NULL; /* server connects to client */
537 ecam->iface.Disconnected = NULL;
538 ecam->iface.Terminated = ecam_plugin_terminated;
539 ecam->iface.Attached = ecam_plugin_attached;
540 ecam->iface.Detached = ecam_plugin_detached;
541
542 /* TODO: camera redirect only supported for platforms with Video4Linux */
543#if defined(WITH_V4L)
544 ecam->subsystem = "v4l";
545#else
546 ecam->subsystem = NULL;
547#endif
548
549 if (ecam->subsystem)
550 {
551 if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem, NULL /*args*/)))
552 {
553 WLog_ERR(TAG,
554 "Unable to load camera redirection subsystem %s because of error %" PRIu32 "",
555 ecam->subsystem, error);
556 goto out;
557 }
558 }
559
560 error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
561 if (error == CHANNEL_RC_OK)
562 return error;
563
564out:
565 ecam_plugin_terminated(&ecam->iface);
566 return error;
567}
Definition camera.h:247
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:58