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(nullptr, 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(nullptr, 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
89 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Length(out));
90
91 WLog_DBG(TAG, "ChannelId=%" PRIu32 ", MessageId=0x%02" PRIx8 ", Length=%" PRIu32,
92 hchannel->channel_mgr->GetChannelId(hchannel->channel), msg, len);
93
94 const UINT error =
95 hchannel->channel->Write(hchannel->channel, len, Stream_Buffer(out), nullptr);
96
97 if (freeStream)
98 Stream_Free(out, TRUE);
99
100 return error;
101}
102
108static UINT ecam_send_device_added_notification(CameraPlugin* ecam,
109 GENERIC_CHANNEL_CALLBACK* hchannel,
110 const char* deviceName, const char* channelName)
111{
112 CAM_MSG_ID msg = CAM_MSG_ID_DeviceAddedNotification;
113
114 WINPR_ASSERT(ecam);
115
116 wStream* s = Stream_New(nullptr, 256);
117 if (!s)
118 {
119 WLog_ERR(TAG, "Stream_New failed!");
120 return ERROR_NOT_ENOUGH_MEMORY;
121 }
122
123 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, ecam->version));
124 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, msg));
125
126 size_t devNameLen = strlen(deviceName);
127 if (Stream_Write_UTF16_String_From_UTF8(s, devNameLen + 1, deviceName, devNameLen, TRUE) < 0)
128 {
129 Stream_Free(s, TRUE);
130 return ERROR_INTERNAL_ERROR;
131 }
132 Stream_Write(s, channelName, strlen(channelName) + 1);
133
134 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
135}
136
142static UINT ecam_ihal_device_added_callback(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
143 const char* deviceId, const char* deviceName)
144{
145 WLog_DBG(TAG, "deviceId=%s, deviceName=%s", deviceId, deviceName);
146
147 if (!HashTable_ContainsKey(ecam->devices, deviceId))
148 {
149 CameraDevice* dev = ecam_dev_create(ecam, deviceId, deviceName);
150 if (!HashTable_Insert(ecam->devices, deviceId, dev))
151 {
152 ecam_dev_destroy(dev);
153 return ERROR_INTERNAL_ERROR;
154 }
155 }
156 else
157 {
158 WLog_DBG(TAG, "Device %s already exists", deviceId);
159 }
160
161 ecam_send_device_added_notification(ecam, hchannel, deviceName, deviceId /*channelName*/);
162
163 return CHANNEL_RC_OK;
164}
165
171static UINT ecam_enumerate_devices(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
172{
173 return ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
174}
175
181static UINT ecam_process_select_version_response(CameraPlugin* ecam,
182 GENERIC_CHANNEL_CALLBACK* hchannel,
183 WINPR_ATTR_UNUSED wStream* s, BYTE serverVersion)
184{
185 const BYTE clientVersion = ECAM_PROTO_VERSION;
186
187 /* check remaining s capacity */
188
189 WLog_DBG(TAG, "ServerVersion=%" PRIu8 ", ClientVersion=%" PRIu8, serverVersion, clientVersion);
190
191 if (serverVersion > clientVersion)
192 {
193 WLog_ERR(TAG,
194 "Incompatible protocol version server=%" PRIu8 ", client supports version=%" PRIu8,
195 serverVersion, clientVersion);
196 return CHANNEL_RC_OK;
197 }
198 ecam->version = serverVersion;
199
200 if (ecam->ihal)
201 ecam_enumerate_devices(ecam, hchannel);
202 else
203 WLog_ERR(TAG, "No HAL registered");
204
205 return CHANNEL_RC_OK;
206}
207
213static UINT ecam_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
214{
215 UINT error = CHANNEL_RC_OK;
216 BYTE version = 0;
217 BYTE messageId = 0;
218 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
219
220 if (!hchannel || !data)
221 return ERROR_INVALID_PARAMETER;
222
223 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
224
225 if (!ecam)
226 return ERROR_INTERNAL_ERROR;
227
228 if (!Stream_CheckAndLogRequiredCapacity(TAG, data, CAM_HEADER_SIZE))
229 return ERROR_NO_DATA;
230
231 Stream_Read_UINT8(data, version);
232 Stream_Read_UINT8(data, messageId);
233 WLog_DBG(TAG, "ChannelId=%" PRIu32 ", MessageId=0x%02" PRIx8 ", Version=%d",
234 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
235
236 switch (messageId)
237 {
238 case CAM_MSG_ID_SelectVersionResponse:
239 error = ecam_process_select_version_response(ecam, hchannel, data, version);
240 break;
241
242 default:
243 WLog_WARN(TAG, "unknown MessageId=0x%02" PRIx8 "", messageId);
244 error = ERROR_INVALID_DATA;
245 ecam_channel_send_error_response(ecam, hchannel, CAM_ERROR_CODE_OperationNotSupported);
246 break;
247 }
248
249 return error;
250}
251
257static UINT ecam_on_open(IWTSVirtualChannelCallback* pChannelCallback)
258{
259 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
260 WINPR_ASSERT(hchannel);
261
262 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
263 WINPR_ASSERT(ecam);
264
265 WLog_DBG(TAG, "entered");
266 return ecam_channel_send_generic_msg(ecam, hchannel, CAM_MSG_ID_SelectVersionRequest);
267}
268
274static UINT ecam_on_close(IWTSVirtualChannelCallback* pChannelCallback)
275{
276 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
277 WINPR_ASSERT(hchannel);
278
279 WLog_DBG(TAG, "entered");
280
281 free(hchannel);
282 return CHANNEL_RC_OK;
283}
284
290static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
291 IWTSVirtualChannel* pChannel,
292 WINPR_ATTR_UNUSED BYTE* Data,
293 WINPR_ATTR_UNUSED BOOL* pbAccept,
294 IWTSVirtualChannelCallback** ppCallback)
295{
296 GENERIC_LISTENER_CALLBACK* hlistener = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
297
298 if (!hlistener || !hlistener->plugin)
299 return ERROR_INTERNAL_ERROR;
300
301 WLog_DBG(TAG, "entered");
302 GENERIC_CHANNEL_CALLBACK* hchannel =
304
305 if (!hchannel)
306 {
307 WLog_ERR(TAG, "calloc failed!");
308 return CHANNEL_RC_NO_MEMORY;
309 }
310
311 hchannel->iface.OnDataReceived = ecam_on_data_received;
312 hchannel->iface.OnOpen = ecam_on_open;
313 hchannel->iface.OnClose = ecam_on_close;
314 hchannel->plugin = hlistener->plugin;
315 hchannel->channel_mgr = hlistener->channel_mgr;
316 hchannel->channel = pChannel;
317 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
318 return CHANNEL_RC_OK;
319}
320
321static void ecam_dev_destroy_pv(void* obj)
322{
323 CameraDevice* dev = obj;
324 ecam_dev_destroy(dev);
325}
326
332static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
333{
334 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
335
336 WLog_DBG(TAG, "entered");
337
338 if (!ecam || !pChannelMgr)
339 return ERROR_INVALID_PARAMETER;
340
341 if (ecam->initialized)
342 {
343 WLog_ERR(TAG, "[%s] plugin initialized twice, aborting", RDPECAM_CONTROL_DVC_CHANNEL_NAME);
344 return ERROR_INVALID_DATA;
345 }
346
347 ecam->version = ECAM_PROTO_VERSION;
348
349 ecam->devices = HashTable_New(FALSE);
350 if (!ecam->devices)
351 {
352 WLog_ERR(TAG, "HashTable_New failed!");
353 return CHANNEL_RC_NO_MEMORY;
354 }
355
356 if (!HashTable_SetupForStringData(ecam->devices, FALSE))
357 {
358 HashTable_Free(ecam->devices);
359 ecam->devices = nullptr;
360 return ERROR_INTERNAL_ERROR;
361 }
362
363 wObject* obj = HashTable_ValueObject(ecam->devices);
364 WINPR_ASSERT(obj);
365 obj->fnObjectFree = ecam_dev_destroy_pv;
366
367 ecam->hlistener = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
368
369 if (!ecam->hlistener)
370 {
371 WLog_ERR(TAG, "calloc failed!");
372 return CHANNEL_RC_NO_MEMORY;
373 }
374
375 ecam->hlistener->iface.OnNewChannelConnection = ecam_on_new_channel_connection;
376 ecam->hlistener->plugin = pPlugin;
377 ecam->hlistener->channel_mgr = pChannelMgr;
378 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, RDPECAM_CONTROL_DVC_CHANNEL_NAME, 0,
379 &ecam->hlistener->iface, &ecam->listener);
380
381 ecam->initialized = (rc == CHANNEL_RC_OK);
382 return rc;
383}
384
390static UINT ecam_plugin_terminated(IWTSPlugin* pPlugin)
391{
392 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
393
394 if (!ecam)
395 return ERROR_INVALID_DATA;
396
397 WLog_DBG(TAG, "entered");
398
399 if (ecam->hlistener)
400 {
401 IWTSVirtualChannelManager* mgr = ecam->hlistener->channel_mgr;
402 if (mgr)
403 IFCALL(mgr->DestroyListener, mgr, ecam->listener);
404 }
405
406 free(ecam->hlistener);
407
408 HashTable_Free(ecam->devices);
409
410 UINT rc = CHANNEL_RC_OK;
411 if (ecam->ihal)
412 rc = ecam->ihal->Free(ecam->ihal);
413
414 free(ecam);
415 return rc;
416}
417
423static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
424{
425 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
426 UINT error = CHANNEL_RC_OK;
427
428 if (!ecam)
429 return ERROR_INVALID_PARAMETER;
430
431 ecam->attached = TRUE;
432 return error;
433}
434
440static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
441{
442 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
443 UINT error = CHANNEL_RC_OK;
444
445 if (!ecam)
446 return ERROR_INVALID_PARAMETER;
447
448 ecam->attached = FALSE;
449 return error;
450}
451
457static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
458{
459 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
460
461 WINPR_ASSERT(ecam);
462
463 if (ecam->ihal)
464 {
465 WLog_DBG(TAG, "already registered");
466 return ERROR_ALREADY_EXISTS;
467 }
468
469 WLog_DBG(TAG, "HAL registered");
470 ecam->ihal = ihal;
471 return CHANNEL_RC_OK;
472}
473
479static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADDIN_ARGV* args)
480{
481 WINPR_ASSERT(ecam);
482
483 FREERDP_CAMERA_HAL_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
484 UINT error = ERROR_INTERNAL_ERROR;
485 union
486 {
487 PVIRTUALCHANNELENTRY pvce;
488 const PFREERDP_CAMERA_HAL_ENTRY entry;
489 } cnv;
490 cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name, nullptr, 0);
491
492 if (cnv.entry == nullptr)
493 {
494 WLog_ERR(TAG,
495 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
496 name);
497 return ERROR_INVALID_FUNCTION;
498 }
499
500 entryPoints.plugin = &ecam->iface;
501 entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
502 entryPoints.args = args;
503 entryPoints.ecam = ecam;
504
505 error = cnv.entry(&entryPoints);
506 if (error)
507 {
508 WLog_ERR(TAG, "%s entry returned error %" PRIu32 ".", name, error);
509 return error;
510 }
511
512 WLog_INFO(TAG, "Loaded %s HAL for ecam", name);
513 return CHANNEL_RC_OK;
514}
515
521FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
522{
523 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
524
525 WINPR_ASSERT(pEntryPoints);
526 WINPR_ASSERT(pEntryPoints->GetPlugin);
527 CameraPlugin* ecam = (CameraPlugin*)pEntryPoints->GetPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME);
528
529 if (ecam != nullptr)
530 return CHANNEL_RC_ALREADY_INITIALIZED;
531
532 ecam = (CameraPlugin*)calloc(1, sizeof(CameraPlugin));
533
534 if (!ecam)
535 {
536 WLog_ERR(TAG, "calloc failed!");
537 return CHANNEL_RC_NO_MEMORY;
538 }
539
540 ecam->attached = TRUE;
541 ecam->iface.Initialize = ecam_plugin_initialize;
542 ecam->iface.Connected = nullptr; /* server connects to client */
543 ecam->iface.Disconnected = nullptr;
544 ecam->iface.Terminated = ecam_plugin_terminated;
545 ecam->iface.Attached = ecam_plugin_attached;
546 ecam->iface.Detached = ecam_plugin_detached;
547
548 /* TODO: camera redirect only supported for platforms with Video4Linux */
549#if defined(WITH_V4L)
550 ecam->subsystem = "v4l";
551#else
552 ecam->subsystem = nullptr;
553#endif
554
555 if (ecam->subsystem)
556 {
557 if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem, nullptr /*args*/)))
558 {
559 WLog_ERR(TAG,
560 "Unable to load camera redirection subsystem %s because of error %" PRIu32 "",
561 ecam->subsystem, error);
562 goto out;
563 }
564 }
565
566 error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
567 if (error == CHANNEL_RC_OK)
568 return error;
569
570out:
571 ecam_plugin_terminated(&ecam->iface);
572 return error;
573}
Definition camera.h:250
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59