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) || Stream_Write_UTF16_String_From_UTF8(s, (size_t)devNameWLen + 1,
130 deviceName, devNameLen, TRUE) < 0)
131 {
132 Stream_Free(s, TRUE);
133 return ERROR_INTERNAL_ERROR;
134 }
135 Stream_Write(s, channelName, strlen(channelName) + 1);
136
137 return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
138}
139
145static UINT ecam_ihal_device_added_callback(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
146 const char* deviceId, const char* deviceName)
147{
148 WLog_DBG(TAG, "deviceId=%s, deviceName=%s", deviceId, deviceName);
149
150 if (!HashTable_ContainsKey(ecam->devices, deviceId))
151 {
152 CameraDevice* dev = ecam_dev_create(ecam, deviceId, deviceName);
153 if (!HashTable_Insert(ecam->devices, deviceId, dev))
154 {
155 ecam_dev_destroy(dev);
156 return ERROR_INTERNAL_ERROR;
157 }
158 }
159 else
160 {
161 WLog_DBG(TAG, "Device %s already exists", deviceId);
162 }
163
164 ecam_send_device_added_notification(ecam, hchannel, deviceName, deviceId /*channelName*/);
165
166 return CHANNEL_RC_OK;
167}
168
174static UINT ecam_enumerate_devices(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
175{
176 return ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
177}
178
184static UINT ecam_process_select_version_response(CameraPlugin* ecam,
185 GENERIC_CHANNEL_CALLBACK* hchannel,
186 WINPR_ATTR_UNUSED wStream* s, BYTE serverVersion)
187{
188 const BYTE clientVersion = ECAM_PROTO_VERSION;
189
190 /* check remaining s capacity */
191
192 WLog_DBG(TAG, "ServerVersion=%" PRIu8 ", ClientVersion=%" PRIu8, serverVersion, clientVersion);
193
194 if (serverVersion > clientVersion)
195 {
196 WLog_ERR(TAG,
197 "Incompatible protocol version server=%" PRIu8 ", client supports version=%" PRIu8,
198 serverVersion, clientVersion);
199 return CHANNEL_RC_OK;
200 }
201 ecam->version = serverVersion;
202
203 if (ecam->ihal)
204 ecam_enumerate_devices(ecam, hchannel);
205 else
206 WLog_ERR(TAG, "No HAL registered");
207
208 return CHANNEL_RC_OK;
209}
210
216static UINT ecam_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
217{
218 UINT error = CHANNEL_RC_OK;
219 BYTE version = 0;
220 BYTE messageId = 0;
221 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
222
223 if (!hchannel || !data)
224 return ERROR_INVALID_PARAMETER;
225
226 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
227
228 if (!ecam)
229 return ERROR_INTERNAL_ERROR;
230
231 if (!Stream_CheckAndLogRequiredLength(TAG, data, CAM_HEADER_SIZE))
232 return ERROR_NO_DATA;
233
234 Stream_Read_UINT8(data, version);
235 Stream_Read_UINT8(data, messageId);
236 WLog_DBG(TAG, "ChannelId=%" PRIu32 ", MessageId=0x%02" PRIx8 ", Version=%d",
237 hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
238
239 switch (messageId)
240 {
241 case CAM_MSG_ID_SelectVersionResponse:
242 error = ecam_process_select_version_response(ecam, hchannel, data, version);
243 break;
244
245 default:
246 WLog_WARN(TAG, "unknown MessageId=0x%02" PRIx8 "", messageId);
247 error = ERROR_INVALID_DATA;
248 ecam_channel_send_error_response(ecam, hchannel, CAM_ERROR_CODE_OperationNotSupported);
249 break;
250 }
251
252 return error;
253}
254
260static UINT ecam_on_open(IWTSVirtualChannelCallback* pChannelCallback)
261{
262 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
263 WINPR_ASSERT(hchannel);
264
265 CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
266 WINPR_ASSERT(ecam);
267
268 WLog_DBG(TAG, "entered");
269 return ecam_channel_send_generic_msg(ecam, hchannel, CAM_MSG_ID_SelectVersionRequest);
270}
271
277static UINT ecam_on_close(IWTSVirtualChannelCallback* pChannelCallback)
278{
279 GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
280 WINPR_ASSERT(hchannel);
281
282 WLog_DBG(TAG, "entered");
283
284 free(hchannel);
285 return CHANNEL_RC_OK;
286}
287
293static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
294 IWTSVirtualChannel* pChannel,
295 WINPR_ATTR_UNUSED BYTE* Data,
296 WINPR_ATTR_UNUSED BOOL* pbAccept,
297 IWTSVirtualChannelCallback** ppCallback)
298{
299 GENERIC_LISTENER_CALLBACK* hlistener = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
300
301 if (!hlistener || !hlistener->plugin)
302 return ERROR_INTERNAL_ERROR;
303
304 WLog_DBG(TAG, "entered");
305 GENERIC_CHANNEL_CALLBACK* hchannel =
307
308 if (!hchannel)
309 {
310 WLog_ERR(TAG, "calloc failed!");
311 return CHANNEL_RC_NO_MEMORY;
312 }
313
314 hchannel->iface.OnDataReceived = ecam_on_data_received;
315 hchannel->iface.OnOpen = ecam_on_open;
316 hchannel->iface.OnClose = ecam_on_close;
317 hchannel->plugin = hlistener->plugin;
318 hchannel->channel_mgr = hlistener->channel_mgr;
319 hchannel->channel = pChannel;
320 *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
321 return CHANNEL_RC_OK;
322}
323
324static void ecam_dev_destroy_pv(void* obj)
325{
326 CameraDevice* dev = obj;
327 ecam_dev_destroy(dev);
328}
329
335static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
336{
337 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
338
339 WLog_DBG(TAG, "entered");
340
341 if (!ecam || !pChannelMgr)
342 return ERROR_INVALID_PARAMETER;
343
344 if (ecam->initialized)
345 {
346 WLog_ERR(TAG, "[%s] plugin initialized twice, aborting", RDPECAM_CONTROL_DVC_CHANNEL_NAME);
347 return ERROR_INVALID_DATA;
348 }
349
350 ecam->version = ECAM_PROTO_VERSION;
351
352 ecam->devices = HashTable_New(FALSE);
353 if (!ecam->devices)
354 {
355 WLog_ERR(TAG, "HashTable_New failed!");
356 return CHANNEL_RC_NO_MEMORY;
357 }
358
359 if (!HashTable_SetupForStringData(ecam->devices, FALSE))
360 {
361 HashTable_Free(ecam->devices);
362 ecam->devices = nullptr;
363 return ERROR_INTERNAL_ERROR;
364 }
365
366 wObject* obj = HashTable_ValueObject(ecam->devices);
367 WINPR_ASSERT(obj);
368 obj->fnObjectFree = ecam_dev_destroy_pv;
369
370 ecam->hlistener = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
371
372 if (!ecam->hlistener)
373 {
374 WLog_ERR(TAG, "calloc failed!");
375 return CHANNEL_RC_NO_MEMORY;
376 }
377
378 ecam->hlistener->iface.OnNewChannelConnection = ecam_on_new_channel_connection;
379 ecam->hlistener->plugin = pPlugin;
380 ecam->hlistener->channel_mgr = pChannelMgr;
381 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, RDPECAM_CONTROL_DVC_CHANNEL_NAME, 0,
382 &ecam->hlistener->iface, &ecam->listener);
383
384 ecam->initialized = (rc == CHANNEL_RC_OK);
385 return rc;
386}
387
393static UINT ecam_plugin_terminated(IWTSPlugin* pPlugin)
394{
395 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
396
397 if (!ecam)
398 return ERROR_INVALID_DATA;
399
400 WLog_DBG(TAG, "entered");
401
402 if (ecam->hlistener)
403 {
404 IWTSVirtualChannelManager* mgr = ecam->hlistener->channel_mgr;
405 if (mgr)
406 IFCALL(mgr->DestroyListener, mgr, ecam->listener);
407 }
408
409 free(ecam->hlistener);
410
411 HashTable_Free(ecam->devices);
412
413 UINT rc = CHANNEL_RC_OK;
414 if (ecam->ihal)
415 rc = ecam->ihal->Free(ecam->ihal);
416
417 free(ecam);
418 return rc;
419}
420
426static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
427{
428 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
429 UINT error = CHANNEL_RC_OK;
430
431 if (!ecam)
432 return ERROR_INVALID_PARAMETER;
433
434 ecam->attached = TRUE;
435 return error;
436}
437
443static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
444{
445 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
446 UINT error = CHANNEL_RC_OK;
447
448 if (!ecam)
449 return ERROR_INVALID_PARAMETER;
450
451 ecam->attached = FALSE;
452 return error;
453}
454
460static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
461{
462 CameraPlugin* ecam = (CameraPlugin*)pPlugin;
463
464 WINPR_ASSERT(ecam);
465
466 if (ecam->ihal)
467 {
468 WLog_DBG(TAG, "already registered");
469 return ERROR_ALREADY_EXISTS;
470 }
471
472 WLog_DBG(TAG, "HAL registered");
473 ecam->ihal = ihal;
474 return CHANNEL_RC_OK;
475}
476
482static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADDIN_ARGV* args)
483{
484 WINPR_ASSERT(ecam);
485
486 FREERDP_CAMERA_HAL_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
487 UINT error = ERROR_INTERNAL_ERROR;
488 union
489 {
490 PVIRTUALCHANNELENTRY pvce;
491 const PFREERDP_CAMERA_HAL_ENTRY entry;
492 } cnv;
493 cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name, nullptr, 0);
494
495 if (cnv.entry == nullptr)
496 {
497 WLog_ERR(TAG,
498 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
499 name);
500 return ERROR_INVALID_FUNCTION;
501 }
502
503 entryPoints.plugin = &ecam->iface;
504 entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
505 entryPoints.args = args;
506 entryPoints.ecam = ecam;
507
508 error = cnv.entry(&entryPoints);
509 if (error)
510 {
511 WLog_ERR(TAG, "%s entry returned error %" PRIu32 ".", name, error);
512 return error;
513 }
514
515 WLog_INFO(TAG, "Loaded %s HAL for ecam", name);
516 return CHANNEL_RC_OK;
517}
518
524FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
525{
526 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
527
528 WINPR_ASSERT(pEntryPoints);
529 WINPR_ASSERT(pEntryPoints->GetPlugin);
530 CameraPlugin* ecam = (CameraPlugin*)pEntryPoints->GetPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME);
531
532 if (ecam != nullptr)
533 return CHANNEL_RC_ALREADY_INITIALIZED;
534
535 ecam = (CameraPlugin*)calloc(1, sizeof(CameraPlugin));
536
537 if (!ecam)
538 {
539 WLog_ERR(TAG, "calloc failed!");
540 return CHANNEL_RC_NO_MEMORY;
541 }
542
543 ecam->attached = TRUE;
544 ecam->iface.Initialize = ecam_plugin_initialize;
545 ecam->iface.Connected = nullptr; /* server connects to client */
546 ecam->iface.Disconnected = nullptr;
547 ecam->iface.Terminated = ecam_plugin_terminated;
548 ecam->iface.Attached = ecam_plugin_attached;
549 ecam->iface.Detached = ecam_plugin_detached;
550
551 /* TODO: camera redirect only supported for platforms with Video4Linux or Android */
552#if defined(WITH_V4L)
553 ecam->subsystem = "v4l";
554#elif defined(__ANDROID__)
555 ecam->subsystem = "android";
556#else
557 ecam->subsystem = nullptr;
558#endif
559
560 if (ecam->subsystem)
561 {
562 if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem, nullptr /*args*/)))
563 {
564 WLog_ERR(TAG,
565 "Unable to load camera redirection subsystem %s because of error %" PRIu32 "",
566 ecam->subsystem, error);
567 goto out;
568 }
569 }
570
571 error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
572 if (error == CHANNEL_RC_OK)
573 return error;
574
575out:
576 ecam_plugin_terminated(&ecam->iface);
577 return error;
578}
Definition camera.h:221
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59