FreeRDP
Loading...
Searching...
No Matches
camera_device_enum_main.c
1
20#include <winpr/assert.h>
21#include <winpr/cast.h>
22#include <winpr/string.h>
23
24#include "camera.h"
25
26#define TAG CHANNELS_TAG("rdpecam-enum.client")
27
33UINT ecam_channel_send_error_response(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
34 CAM_ERROR_CODE code)
35{
36 CAM_MSG_ID msg = CAM_MSG_ID_ErrorResponse;
37
38 WINPR_ASSERT(ecam);
39
40 wStream* s = Stream_New(nullptr, CAM_HEADER_SIZE + 4);
41 if (!s)
42 {
43 WLog_ERR(TAG, "Stream_New failed!");
44 return ERROR_NOT_ENOUGH_MEMORY;
45 }
46
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);
50
51 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
52}
53
59UINT ecam_channel_send_generic_msg(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
60 CAM_MSG_ID msg)
61{
62 WINPR_ASSERT(ecam);
63
64 wStream* s = Stream_New(nullptr, CAM_HEADER_SIZE);
65 if (!s)
66 {
67 WLog_ERR(TAG, "Stream_New failed!");
68 return ERROR_NOT_ENOUGH_MEMORY;
69 }
70
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));
73
74 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
75}
76
82UINT ecam_channel_write(WINPR_ATTR_UNUSED CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
83 CAM_MSG_ID msg, wStream* out, BOOL freeStream)
84{
85 if (!hchannel || !out)
86 return ERROR_INVALID_PARAMETER;
87
88 Stream_SealLength(out);
89
90 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Length(out));
91
92 WLog_DBG(TAG, "ChannelId=%" PRIu32 ", MessageId=0x%02" PRIx8 ", Length=%" PRIu32,
93 hchannel->channel_mgr->GetChannelId(hchannel->channel), msg, len);
94
95 const UINT error =
96 hchannel->channel->Write(hchannel->channel, len, Stream_Buffer(out), nullptr);
97
98 if (freeStream)
99 Stream_Free(out, TRUE);
100
101 return error;
102}
103
109static UINT ecam_send_device_added_notification(CameraPlugin* ecam,
110 GENERIC_CHANNEL_CALLBACK* hchannel,
111 const char* deviceName, const char* channelName)
112{
113 CAM_MSG_ID msg = CAM_MSG_ID_DeviceAddedNotification;
114
115 WINPR_ASSERT(ecam);
116
117 wStream* s = Stream_New(nullptr, 256);
118 if (!s)
119 {
120 WLog_ERR(TAG, "Stream_New failed!");
121 return ERROR_NOT_ENOUGH_MEMORY;
122 }
123
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));
126
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)
132 {
133 Stream_Free(s, TRUE);
134 return ERROR_INTERNAL_ERROR;
135 }
136 Stream_Write(s, channelName, strlen(channelName) + 1);
137
138 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
139}
140
146static UINT ecam_ihal_device_added_callback(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
147 const char* deviceId, const char* deviceName)
148{
149 WLog_DBG(TAG, "deviceId=%s, deviceName=%s", deviceId, deviceName);
150
151 if (!HashTable_ContainsKey(ecam->devices, deviceId))
152 {
153 CameraDevice* dev = ecam_dev_create(ecam, deviceId, deviceName);
154 if (!HashTable_Insert(ecam->devices, deviceId, dev))
155 {
156 ecam_dev_destroy(dev);
157 return ERROR_INTERNAL_ERROR;
158 }
159 }
160 else
161 {
162 WLog_DBG(TAG, "Device %s already exists", deviceId);
163 }
164
165 ecam_send_device_added_notification(ecam, hchannel, deviceName, deviceId /*channelName*/);
166
167 return CHANNEL_RC_OK;
168}
169
175static UINT ecam_enumerate_devices(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
176{
177 return ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
178}
179
185static UINT ecam_process_select_version_response(CameraPlugin* ecam,
186 GENERIC_CHANNEL_CALLBACK* hchannel,
187 WINPR_ATTR_UNUSED wStream* s, BYTE serverVersion)
188{
189 const BYTE clientVersion = ECAM_PROTO_VERSION;
190
191 /* check remaining s capacity */
192
193 WLog_DBG(TAG, "ServerVersion=%" PRIu8 ", ClientVersion=%" PRIu8, serverVersion, clientVersion);
194
195 if (serverVersion > clientVersion)
196 {
197 WLog_ERR(TAG,
198 "Incompatible protocol version server=%" PRIu8 ", client supports version=%" PRIu8,
199 serverVersion, clientVersion);
200 return CHANNEL_RC_OK;
201 }
202 ecam->version = serverVersion;
203
204 if (ecam->ihal)
205 ecam_enumerate_devices(ecam, hchannel);
206 else
207 WLog_ERR(TAG, "No HAL registered");
208
209 return CHANNEL_RC_OK;
210}
211
217static UINT ecam_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
218{
219 UINT error = CHANNEL_RC_OK;
220 BYTE version = 0;
221 BYTE messageId = 0;
222 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
223
224 if (!hchannel || !data)
225 return ERROR_INVALID_PARAMETER;
226
227 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
228
229 if (!ecam)
230 return ERROR_INTERNAL_ERROR;
231
232 if (!Stream_CheckAndLogRequiredLength(TAG, data, CAM_HEADER_SIZE))
233 return ERROR_NO_DATA;
234
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);
239
240 switch (messageId)
241 {
242 case CAM_MSG_ID_SelectVersionResponse:
243 error = ecam_process_select_version_response(ecam, hchannel, data, version);
244 break;
245
246 default:
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);
250 break;
251 }
252
253 return error;
254}
255
261static UINT ecam_on_open(IWTSVirtualChannelCallback* pChannelCallback)
262{
263 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
264 WINPR_ASSERT(hchannel);
265
266 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
267 WINPR_ASSERT(ecam);
268
269 WLog_DBG(TAG, "entered");
270 return ecam_channel_send_generic_msg(ecam, hchannel, CAM_MSG_ID_SelectVersionRequest);
271}
272
278static UINT ecam_on_close(IWTSVirtualChannelCallback* pChannelCallback)
279{
280 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
281 WINPR_ASSERT(hchannel);
282
283 WLog_DBG(TAG, "entered");
284
285 free(hchannel);
286 return CHANNEL_RC_OK;
287}
288
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)
299{
300 GENERIC_LISTENER_CALLBACK* hlistener = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
301
302 if (!hlistener || !hlistener->plugin)
303 return ERROR_INTERNAL_ERROR;
304
305 WLog_DBG(TAG, "entered");
306 GENERIC_CHANNEL_CALLBACK* hchannel =
308
309 if (!hchannel)
310 {
311 WLog_ERR(TAG, "calloc failed!");
312 return CHANNEL_RC_NO_MEMORY;
313 }
314
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;
323}
324
325static void ecam_dev_destroy_pv(void* obj)
326{
327 CameraDevice* dev = obj;
328 ecam_dev_destroy(dev);
329}
330
336static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
337{
338 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
339
340 WLog_DBG(TAG, "entered");
341
342 if (!ecam || !pChannelMgr)
343 return ERROR_INVALID_PARAMETER;
344
345 if (ecam->initialized)
346 {
347 WLog_ERR(TAG, "[%s] plugin initialized twice, aborting", RDPECAM_CONTROL_DVC_CHANNEL_NAME);
348 return ERROR_INVALID_DATA;
349 }
350
351 ecam->version = ECAM_PROTO_VERSION;
352
353 ecam->devices = HashTable_New(FALSE);
354 if (!ecam->devices)
355 {
356 WLog_ERR(TAG, "HashTable_New failed!");
357 return CHANNEL_RC_NO_MEMORY;
358 }
359
360 if (!HashTable_SetupForStringData(ecam->devices, FALSE))
361 {
362 HashTable_Free(ecam->devices);
363 ecam->devices = nullptr;
364 return ERROR_INTERNAL_ERROR;
365 }
366
367 wObject* obj = HashTable_ValueObject(ecam->devices);
368 WINPR_ASSERT(obj);
369 obj->fnObjectFree = ecam_dev_destroy_pv;
370
371 ecam->hlistener = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
372
373 if (!ecam->hlistener)
374 {
375 WLog_ERR(TAG, "calloc failed!");
376 return CHANNEL_RC_NO_MEMORY;
377 }
378
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);
384
385 ecam->initialized = (rc == CHANNEL_RC_OK);
386 return rc;
387}
388
394static UINT ecam_plugin_terminated(IWTSPlugin* pPlugin)
395{
396 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
397
398 if (!ecam)
399 return ERROR_INVALID_DATA;
400
401 WLog_DBG(TAG, "entered");
402
403 if (ecam->hlistener)
404 {
405 IWTSVirtualChannelManager* mgr = ecam->hlistener->channel_mgr;
406 if (mgr)
407 IFCALL(mgr->DestroyListener, mgr, ecam->listener);
408 }
409
410 free(ecam->hlistener);
411
412 HashTable_Free(ecam->devices);
413
414 UINT rc = CHANNEL_RC_OK;
415 if (ecam->ihal)
416 rc = ecam->ihal->Free(ecam->ihal);
417
418 free(ecam);
419 return rc;
420}
421
427static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
428{
429 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
430 UINT error = CHANNEL_RC_OK;
431
432 if (!ecam)
433 return ERROR_INVALID_PARAMETER;
434
435 ecam->attached = TRUE;
436 return error;
437}
438
444static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
445{
446 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
447 UINT error = CHANNEL_RC_OK;
448
449 if (!ecam)
450 return ERROR_INVALID_PARAMETER;
451
452 ecam->attached = FALSE;
453 return error;
454}
455
461static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
462{
463 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
464
465 WINPR_ASSERT(ecam);
466
467 if (ecam->ihal)
468 {
469 WLog_DBG(TAG, "already registered");
470 return ERROR_ALREADY_EXISTS;
471 }
472
473 WLog_DBG(TAG, "HAL registered");
474 ecam->ihal = ihal;
475 return CHANNEL_RC_OK;
476}
477
483static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADDIN_ARGV* args)
484{
485 WINPR_ASSERT(ecam);
486
487 FREERDP_CAMERA_HAL_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
488 UINT error = ERROR_INTERNAL_ERROR;
489 union
490 {
491 PVIRTUALCHANNELENTRY pvce;
492 const PFREERDP_CAMERA_HAL_ENTRY entry;
493 } cnv;
494 cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name, nullptr, 0);
495
496 if (cnv.entry == nullptr)
497 {
498 WLog_ERR(TAG,
499 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
500 name);
501 return ERROR_INVALID_FUNCTION;
502 }
503
504 entryPoints.plugin = &ecam->iface;
505 entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
506 entryPoints.args = args;
507 entryPoints.ecam = ecam;
508
509 error = cnv.entry(&entryPoints);
510 if (error)
511 {
512 WLog_ERR(TAG, "%s entry returned error %" PRIu32 ".", name, error);
513 return error;
514 }
515
516 WLog_INFO(TAG, "Loaded %s HAL for ecam", name);
517 return CHANNEL_RC_OK;
518}
519
525FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
526{
527 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
528
529 WINPR_ASSERT(pEntryPoints);
530 WINPR_ASSERT(pEntryPoints->GetPlugin);
531 CameraPlugin* ecam = (CameraPlugin*)pEntryPoints->GetPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME);
532
533 if (ecam != nullptr)
534 return CHANNEL_RC_ALREADY_INITIALIZED;
535
536 ecam = (CameraPlugin*)calloc(1, sizeof(CameraPlugin));
537
538 if (!ecam)
539 {
540 WLog_ERR(TAG, "calloc failed!");
541 return CHANNEL_RC_NO_MEMORY;
542 }
543
544 ecam->attached = TRUE;
545 ecam->iface.Initialize = ecam_plugin_initialize;
546 ecam->iface.Connected = nullptr; /* server connects to client */
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;
551
552 /* TODO: camera redirect only supported for platforms with Video4Linux or Android */
553#if defined(WITH_V4L)
554 ecam->subsystem = "v4l";
555#elif defined(__ANDROID__)
556 ecam->subsystem = "android";
557#else
558 ecam->subsystem = nullptr;
559#endif
560
561 if (ecam->subsystem)
562 {
563 if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem, nullptr /*args*/)))
564 {
565 WLog_ERR(TAG,
566 "Unable to load camera redirection subsystem %s because of error %" PRIu32 "",
567 ecam->subsystem, error);
568 goto out;
569 }
570 }
571
572 error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
573 if (error == CHANNEL_RC_OK)
574 return error;
575
576out:
577 ecam_plugin_terminated(&ecam->iface);
578 return error;
579}
Definition camera.h:229
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59