FreeRDP
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 
32 UINT 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 
58 UINT 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 
81 UINT ecam_channel_write(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel, CAM_MSG_ID msg,
82  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=%d, MessageId=0x%02" PRIx8 ", Length=%d",
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 
107 static 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 
141 static 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 
170 static UINT ecam_enumerate_devices(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
171 {
172  ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
173 
174  return CHANNEL_RC_OK;
175 }
176 
182 static UINT ecam_process_select_version_response(CameraPlugin* ecam,
183  GENERIC_CHANNEL_CALLBACK* hchannel, wStream* s,
184  BYTE serverVersion)
185 {
186  const BYTE clientVersion = ECAM_PROTO_VERSION;
187 
188  /* check remaining s capacity */
189 
190  WLog_DBG(TAG, "ServerVersion=%" PRIu8 ", ClientVersion=%" PRIu8, serverVersion, clientVersion);
191 
192  if (serverVersion > clientVersion)
193  {
194  WLog_ERR(TAG,
195  "Incompatible protocol version server=%" PRIu8 ", client supports version=%" PRIu8,
196  serverVersion, clientVersion);
197  return CHANNEL_RC_OK;
198  }
199  ecam->version = serverVersion;
200 
201  if (ecam->ihal)
202  ecam_enumerate_devices(ecam, hchannel);
203  else
204  WLog_ERR(TAG, "No HAL registered");
205 
206  return CHANNEL_RC_OK;
207 }
208 
214 static UINT ecam_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
215 {
216  UINT error = CHANNEL_RC_OK;
217  BYTE version = 0;
218  BYTE messageId = 0;
219  GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
220 
221  if (!hchannel || !data)
222  return ERROR_INVALID_PARAMETER;
223 
224  CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
225 
226  if (!ecam)
227  return ERROR_INTERNAL_ERROR;
228 
229  if (!Stream_CheckAndLogRequiredCapacity(TAG, data, CAM_HEADER_SIZE))
230  return ERROR_NO_DATA;
231 
232  Stream_Read_UINT8(data, version);
233  Stream_Read_UINT8(data, messageId);
234  WLog_DBG(TAG, "ChannelId=%d, MessageId=0x%02" PRIx8 ", Version=%d",
235  hchannel->channel_mgr->GetChannelId(hchannel->channel), messageId, version);
236 
237  switch (messageId)
238  {
239  case CAM_MSG_ID_SelectVersionResponse:
240  error = ecam_process_select_version_response(ecam, hchannel, data, version);
241  break;
242 
243  default:
244  WLog_WARN(TAG, "unknown MessageId=0x%02" PRIx8 "", messageId);
245  error = ERROR_INVALID_DATA;
246  ecam_channel_send_error_response(ecam, hchannel, CAM_ERROR_CODE_OperationNotSupported);
247  break;
248  }
249 
250  return error;
251 }
252 
258 static UINT ecam_on_open(IWTSVirtualChannelCallback* pChannelCallback)
259 {
260  GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
261  WINPR_ASSERT(hchannel);
262 
263  CameraPlugin* ecam = (CameraPlugin*)hchannel->plugin;
264  WINPR_ASSERT(ecam);
265 
266  WLog_DBG(TAG, "entered");
267  return ecam_channel_send_generic_msg(ecam, hchannel, CAM_MSG_ID_SelectVersionRequest);
268 }
269 
275 static UINT ecam_on_close(IWTSVirtualChannelCallback* pChannelCallback)
276 {
277  GENERIC_CHANNEL_CALLBACK* hchannel = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
278  WINPR_ASSERT(hchannel);
279 
280  WLog_DBG(TAG, "entered");
281 
282  free(hchannel);
283  return CHANNEL_RC_OK;
284 }
285 
291 static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
292  IWTSVirtualChannel* pChannel, BYTE* Data, 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 
320 static void ecam_dev_destroy_pv(void* obj)
321 {
322  CameraDevice* dev = obj;
323  ecam_dev_destroy(dev);
324 }
325 
331 static 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 
384 static 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  if (ecam->ihal)
405  ecam->ihal->Free(ecam->ihal);
406 
407  free(ecam);
408  return CHANNEL_RC_OK;
409 }
410 
416 static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
417 {
418  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
419  UINT error = CHANNEL_RC_OK;
420 
421  if (!ecam)
422  return ERROR_INVALID_PARAMETER;
423 
424  ecam->attached = TRUE;
425  return error;
426 }
427 
433 static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
434 {
435  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
436  UINT error = CHANNEL_RC_OK;
437 
438  if (!ecam)
439  return ERROR_INVALID_PARAMETER;
440 
441  ecam->attached = FALSE;
442  return error;
443 }
444 
450 static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
451 {
452  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
453 
454  WINPR_ASSERT(ecam);
455 
456  if (ecam->ihal)
457  {
458  WLog_DBG(TAG, "already registered");
459  return ERROR_ALREADY_EXISTS;
460  }
461 
462  WLog_DBG(TAG, "HAL registered");
463  ecam->ihal = ihal;
464  return CHANNEL_RC_OK;
465 }
466 
472 static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADDIN_ARGV* args)
473 {
474  WINPR_ASSERT(ecam);
475 
476  FREERDP_CAMERA_HAL_ENTRY_POINTS entryPoints = { 0 };
477  UINT error = ERROR_INTERNAL_ERROR;
478  union
479  {
480  PVIRTUALCHANNELENTRY pvce;
481  const PFREERDP_CAMERA_HAL_ENTRY entry;
482  } cnv;
483  cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name, NULL, 0);
484 
485  if (cnv.entry == NULL)
486  {
487  WLog_ERR(TAG,
488  "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
489  name);
490  return ERROR_INVALID_FUNCTION;
491  }
492 
493  entryPoints.plugin = &ecam->iface;
494  entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
495  entryPoints.args = args;
496  entryPoints.ecam = ecam;
497 
498  error = cnv.entry(&entryPoints);
499  if (error)
500  {
501  WLog_ERR(TAG, "%s entry returned error %" PRIu32 ".", name, error);
502  return error;
503  }
504 
505  WLog_INFO(TAG, "Loaded %s HAL for ecam", name);
506  return CHANNEL_RC_OK;
507 }
508 
514 FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
515 {
516  UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
517 
518  WINPR_ASSERT(pEntryPoints);
519  WINPR_ASSERT(pEntryPoints->GetPlugin);
520  CameraPlugin* ecam = (CameraPlugin*)pEntryPoints->GetPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME);
521 
522  if (ecam != NULL)
523  return CHANNEL_RC_ALREADY_INITIALIZED;
524 
525  ecam = (CameraPlugin*)calloc(1, sizeof(CameraPlugin));
526 
527  if (!ecam)
528  {
529  WLog_ERR(TAG, "calloc failed!");
530  return CHANNEL_RC_NO_MEMORY;
531  }
532 
533  ecam->attached = TRUE;
534  ecam->iface.Initialize = ecam_plugin_initialize;
535  ecam->iface.Connected = NULL; /* server connects to client */
536  ecam->iface.Disconnected = NULL;
537  ecam->iface.Terminated = ecam_plugin_terminated;
538  ecam->iface.Attached = ecam_plugin_attached;
539  ecam->iface.Detached = ecam_plugin_detached;
540 
541  /* TODO: camera redirect only supported for platforms with Video4Linux */
542 #if defined(WITH_V4L)
543  ecam->subsystem = "v4l";
544 #else
545  ecam->subsystem = NULL;
546 #endif
547 
548  if (ecam->subsystem)
549  {
550  if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem, NULL /*args*/)))
551  {
552  WLog_ERR(TAG,
553  "Unable to load camera redirection subsystem %s because of error %" PRIu32 "",
554  ecam->subsystem, error);
555  goto out;
556  }
557  }
558 
559  error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
560  if (error == CHANNEL_RC_OK)
561  return error;
562 
563 out:
564  ecam_plugin_terminated(&ecam->iface);
565  return error;
566 }
Definition: camera.h:163
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57