FreeRDP
camera_device_enum_main.c
1 
20 #include <winpr/assert.h>
21 
22 #include "camera.h"
23 
24 #define TAG CHANNELS_TAG("rdpecam-enum.client")
25 
31 UINT ecam_channel_send_error_response(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
32  CAM_ERROR_CODE code)
33 {
34  CAM_MSG_ID msg = CAM_MSG_ID_ErrorResponse;
35 
36  WINPR_ASSERT(ecam);
37 
38  wStream* s = Stream_New(NULL, CAM_HEADER_SIZE + 4);
39  if (!s)
40  {
41  WLog_ERR(TAG, "Stream_New failed!");
42  return ERROR_NOT_ENOUGH_MEMORY;
43  }
44 
45  Stream_Write_UINT8(s, ecam->version);
46  Stream_Write_UINT8(s, msg);
47  Stream_Write_UINT32(s, code);
48 
49  return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
50 }
51 
57 UINT ecam_channel_send_generic_msg(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
58  CAM_MSG_ID msg)
59 {
60  WINPR_ASSERT(ecam);
61 
62  wStream* s = Stream_New(NULL, CAM_HEADER_SIZE);
63  if (!s)
64  {
65  WLog_ERR(TAG, "Stream_New failed!");
66  return ERROR_NOT_ENOUGH_MEMORY;
67  }
68 
69  Stream_Write_UINT8(s, ecam->version);
70  Stream_Write_UINT8(s, msg);
71 
72  return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
73 }
74 
80 UINT ecam_channel_write(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel, CAM_MSG_ID msg,
81  wStream* out, BOOL freeStream)
82 {
83  if (!hchannel || !out)
84  return ERROR_INVALID_PARAMETER;
85 
86  Stream_SealLength(out);
87  WINPR_ASSERT(Stream_Length(out) <= UINT32_MAX);
88 
89  WLog_DBG(TAG, "ChannelId=%d, MessageId=0x%02" PRIx8 ", Length=%d",
90  hchannel->channel_mgr->GetChannelId(hchannel->channel), msg, Stream_Length(out));
91 
92  const UINT error = hchannel->channel->Write(hchannel->channel, (ULONG)Stream_Length(out),
93  Stream_Buffer(out), NULL);
94 
95  if (freeStream)
96  Stream_Free(out, TRUE);
97 
98  return error;
99 }
100 
106 static UINT ecam_send_device_added_notification(CameraPlugin* ecam,
107  GENERIC_CHANNEL_CALLBACK* hchannel,
108  const char* deviceName, const char* channelName)
109 {
110  CAM_MSG_ID msg = CAM_MSG_ID_DeviceAddedNotification;
111 
112  WINPR_ASSERT(ecam);
113 
114  wStream* s = Stream_New(NULL, 256);
115  if (!s)
116  {
117  WLog_ERR(TAG, "Stream_New failed!");
118  return ERROR_NOT_ENOUGH_MEMORY;
119  }
120 
121  Stream_Write_UINT8(s, ecam->version);
122  Stream_Write_UINT8(s, msg);
123 
124  size_t devNameLen = strlen(deviceName);
125  if (Stream_Write_UTF16_String_From_UTF8(s, devNameLen + 1, deviceName, devNameLen, TRUE) < 0)
126  {
127  Stream_Free(s, TRUE);
128  return ERROR_INTERNAL_ERROR;
129  }
130  Stream_Write(s, channelName, strlen(channelName) + 1);
131 
132  return ecam_channel_write(ecam, hchannel, msg, s, TRUE);
133 }
134 
140 static UINT ecam_ihal_device_added_callback(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
141  const char* deviceId, const char* deviceName)
142 {
143  WLog_DBG(TAG, "deviceId=%s, deviceName=%s", deviceId, deviceName);
144 
145  if (!HashTable_ContainsKey(ecam->devices, deviceId))
146  {
147  CameraDevice* dev = ecam_dev_create(ecam, deviceId, deviceName);
148  if (!HashTable_Insert(ecam->devices, deviceId, dev))
149  {
150  ecam_dev_destroy(dev);
151  return ERROR_INTERNAL_ERROR;
152  }
153  }
154  else
155  {
156  WLog_DBG(TAG, "Device %s already exists", deviceId);
157  }
158 
159  ecam_send_device_added_notification(ecam, hchannel, deviceName, deviceId /*channelName*/);
160 
161  return CHANNEL_RC_OK;
162 }
163 
169 static UINT ecam_enumerate_devices(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
170 {
171  ecam->ihal->Enumerate(ecam->ihal, ecam_ihal_device_added_callback, ecam, hchannel);
172 
173  return CHANNEL_RC_OK;
174 }
175 
181 static UINT ecam_process_select_version_response(CameraPlugin* ecam,
182  GENERIC_CHANNEL_CALLBACK* hchannel, wStream* s,
183  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 
213 static 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=%d, 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 
257 static 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 
274 static 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 
290 static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
291  IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
292  IWTSVirtualChannelCallback** ppCallback)
293 {
294  GENERIC_LISTENER_CALLBACK* hlistener = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
295 
296  if (!hlistener || !hlistener->plugin)
297  return ERROR_INTERNAL_ERROR;
298 
299  WLog_DBG(TAG, "entered");
300  GENERIC_CHANNEL_CALLBACK* hchannel =
302 
303  if (!hchannel)
304  {
305  WLog_ERR(TAG, "calloc failed!");
306  return CHANNEL_RC_NO_MEMORY;
307  }
308 
309  hchannel->iface.OnDataReceived = ecam_on_data_received;
310  hchannel->iface.OnOpen = ecam_on_open;
311  hchannel->iface.OnClose = ecam_on_close;
312  hchannel->plugin = hlistener->plugin;
313  hchannel->channel_mgr = hlistener->channel_mgr;
314  hchannel->channel = pChannel;
315  *ppCallback = (IWTSVirtualChannelCallback*)hchannel;
316  return CHANNEL_RC_OK;
317 }
318 
319 static void ecam_dev_destroy_pv(void* obj)
320 {
321  CameraDevice* dev = obj;
322  ecam_dev_destroy(dev);
323 }
324 
330 static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
331 {
332  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
333 
334  WLog_DBG(TAG, "entered");
335 
336  if (!ecam || !pChannelMgr)
337  return ERROR_INVALID_PARAMETER;
338 
339  if (ecam->initialized)
340  {
341  WLog_ERR(TAG, "[%s] plugin initialized twice, aborting", RDPECAM_CONTROL_DVC_CHANNEL_NAME);
342  return ERROR_INVALID_DATA;
343  }
344 
345  ecam->version = ECAM_PROTO_VERSION;
346 
347  ecam->devices = HashTable_New(FALSE);
348  if (!ecam->devices)
349  {
350  WLog_ERR(TAG, "HashTable_New failed!");
351  return CHANNEL_RC_NO_MEMORY;
352  }
353 
354  HashTable_SetupForStringData(ecam->devices, FALSE);
355 
356  wObject* obj = HashTable_ValueObject(ecam->devices);
357  WINPR_ASSERT(obj);
358  obj->fnObjectFree = ecam_dev_destroy_pv;
359 
360  ecam->hlistener = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
361 
362  if (!ecam->hlistener)
363  {
364  WLog_ERR(TAG, "calloc failed!");
365  return CHANNEL_RC_NO_MEMORY;
366  }
367 
368  ecam->hlistener->iface.OnNewChannelConnection = ecam_on_new_channel_connection;
369  ecam->hlistener->plugin = pPlugin;
370  ecam->hlistener->channel_mgr = pChannelMgr;
371  const UINT rc = pChannelMgr->CreateListener(pChannelMgr, RDPECAM_CONTROL_DVC_CHANNEL_NAME, 0,
372  &ecam->hlistener->iface, &ecam->listener);
373 
374  ecam->initialized = (rc == CHANNEL_RC_OK);
375  return rc;
376 }
377 
383 static UINT ecam_plugin_terminated(IWTSPlugin* pPlugin)
384 {
385  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
386 
387  if (!ecam)
388  return ERROR_INVALID_DATA;
389 
390  WLog_DBG(TAG, "entered");
391 
392  if (ecam->hlistener)
393  {
394  IWTSVirtualChannelManager* mgr = ecam->hlistener->channel_mgr;
395  if (mgr)
396  IFCALL(mgr->DestroyListener, mgr, ecam->listener);
397  }
398 
399  free(ecam->hlistener);
400 
401  HashTable_Free(ecam->devices);
402 
403  if (ecam->ihal)
404  ecam->ihal->Free(ecam->ihal);
405 
406  free(ecam);
407  return CHANNEL_RC_OK;
408 }
409 
415 static UINT ecam_plugin_attached(IWTSPlugin* pPlugin)
416 {
417  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
418  UINT error = CHANNEL_RC_OK;
419 
420  if (!ecam)
421  return ERROR_INVALID_PARAMETER;
422 
423  ecam->attached = TRUE;
424  return error;
425 }
426 
432 static UINT ecam_plugin_detached(IWTSPlugin* pPlugin)
433 {
434  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
435  UINT error = CHANNEL_RC_OK;
436 
437  if (!ecam)
438  return ERROR_INVALID_PARAMETER;
439 
440  ecam->attached = FALSE;
441  return error;
442 }
443 
449 static UINT ecam_register_hal_plugin(IWTSPlugin* pPlugin, ICamHal* ihal)
450 {
451  CameraPlugin* ecam = (CameraPlugin*)pPlugin;
452 
453  WINPR_ASSERT(ecam);
454 
455  if (ecam->ihal)
456  {
457  WLog_DBG(TAG, "already registered");
458  return ERROR_ALREADY_EXISTS;
459  }
460 
461  WLog_DBG(TAG, "HAL registered");
462  ecam->ihal = ihal;
463  return CHANNEL_RC_OK;
464 }
465 
471 static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADDIN_ARGV* args)
472 {
473  WINPR_ASSERT(ecam);
474 
475  FREERDP_CAMERA_HAL_ENTRY_POINTS entryPoints = { 0 };
476  UINT error = ERROR_INTERNAL_ERROR;
477  union
478  {
479  PVIRTUALCHANNELENTRY pvce;
480  const PFREERDP_CAMERA_HAL_ENTRY entry;
481  } cnv;
482  cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name, NULL, 0);
483 
484  if (cnv.entry == NULL)
485  {
486  WLog_ERR(TAG,
487  "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
488  name);
489  return ERROR_INVALID_FUNCTION;
490  }
491 
492  entryPoints.plugin = &ecam->iface;
493  entryPoints.pRegisterCameraHal = ecam_register_hal_plugin;
494  entryPoints.args = args;
495  entryPoints.ecam = ecam;
496 
497  error = cnv.entry(&entryPoints);
498  if (error)
499  {
500  WLog_ERR(TAG, "%s entry returned error %" PRIu32 ".", name, error);
501  return error;
502  }
503 
504  WLog_INFO(TAG, "Loaded %s HAL for ecam", name);
505  return CHANNEL_RC_OK;
506 }
507 
513 FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
514 {
515  UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
516 
517  WINPR_ASSERT(pEntryPoints);
518  WINPR_ASSERT(pEntryPoints->GetPlugin);
519  CameraPlugin* ecam = (CameraPlugin*)pEntryPoints->GetPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME);
520 
521  if (ecam != NULL)
522  return CHANNEL_RC_ALREADY_INITIALIZED;
523 
524  ecam = (CameraPlugin*)calloc(1, sizeof(CameraPlugin));
525 
526  if (!ecam)
527  {
528  WLog_ERR(TAG, "calloc failed!");
529  return CHANNEL_RC_NO_MEMORY;
530  }
531 
532  ecam->attached = TRUE;
533  ecam->iface.Initialize = ecam_plugin_initialize;
534  ecam->iface.Connected = NULL; /* server connects to client */
535  ecam->iface.Disconnected = NULL;
536  ecam->iface.Terminated = ecam_plugin_terminated;
537  ecam->iface.Attached = ecam_plugin_attached;
538  ecam->iface.Detached = ecam_plugin_detached;
539 
540  /* TODO: camera redirect only supported for platforms with Video4Linux */
541 #if defined(WITH_V4L)
542  ecam->subsystem = "v4l";
543 #else
544  ecam->subsystem = NULL;
545 #endif
546 
547  if (ecam->subsystem)
548  {
549  if ((error = ecam_load_hal_plugin(ecam, ecam->subsystem, NULL /*args*/)))
550  {
551  WLog_ERR(TAG,
552  "Unable to load camera redirection subsystem %s because of error %" PRIu32 "",
553  ecam->subsystem, error);
554  goto out;
555  }
556  }
557 
558  error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPECAM_CHANNEL_NAME, &ecam->iface);
559  if (error == CHANNEL_RC_OK)
560  return error;
561 
562 out:
563  ecam_plugin_terminated(&ecam->iface);
564  return error;
565 }
Definition: camera.h:163
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57