1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4 
5 #ifdef HAVE_EEZE
6 # include <unistd.h>
7 # include <sys/types.h>
8 # include <sys/stat.h>
9 # include <fcntl.h>
10 # ifdef HAVE_V4L2
11 #  include <sys/ioctl.h>
12 #  include <linux/videodev2.h>
13 # endif
14 # include <Eeze.h>
15 #endif
16 
17 #include <Ecore.h>
18 
19 #include "emotion_private.h"
20 
21 EAPI int EMOTION_WEBCAM_UPDATE = 0;
22 EAPI int EMOTION_WEBCAM_ADD = 0;
23 EAPI int EMOTION_WEBCAM_DEL = 0;
24 
25 typedef struct _Emotion_Webcams Emotion_Webcams;
26 
27 struct _Emotion_Webcams
28 {
29    Eina_List *webcams;
30    Ecore_Idler *idler;
31    Eina_List *check_list;
32    Eina_Bool init : 1;
33 };
34 
35 struct _Emotion_Webcam
36 {
37    EINA_REFCOUNT;
38 
39    const char *syspath;
40    const char *device;
41    const char *name;
42    const char *filename;
43    Eina_Bool in_list : 1;
44 };
45 
46 static Emotion_Webcams *_emotion_webcams = NULL;
47 
48 static void
emotion_webcam_destroy(Emotion_Webcam * ew)49 emotion_webcam_destroy(Emotion_Webcam *ew)
50 {
51    eina_stringshare_del(ew->syspath);
52    eina_stringshare_del(ew->device);
53    eina_stringshare_del(ew->name);
54    free(ew);
55 }
56 
57 #ifdef HAVE_EEZE
58 static Eeze_Udev_Watch *eeze_watcher = NULL;
59 
60 static Eina_Bool
_emotion_check_device(Emotion_Webcam * ew)61 _emotion_check_device(Emotion_Webcam *ew)
62 {
63 #ifdef HAVE_V4L2
64    Emotion_Webcam *check;
65    Eina_List *l;
66    struct v4l2_capability caps;
67    int fd = -1;
68 #endif
69 
70    if (!ew) return EINA_FALSE;
71 #ifdef HAVE_V4L2
72    if (!ew->device) goto on_error;
73 
74    fd = open(ew->filename, O_RDONLY);
75    if (fd < 0) goto on_error;
76 
77    if (ioctl(fd, VIDIOC_QUERYCAP, &caps) == -1) goto on_error;
78 
79    // Likely not a webcam
80    if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE)) goto on_error;
81    if (caps.capabilities &
82        (V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_MODULATOR))
83      goto on_error;
84 
85    EINA_LIST_FOREACH(_emotion_webcams->webcams, l, check)
86      {
87         if (check->device == ew->device) goto on_error;
88      }
89    _emotion_webcams->webcams = eina_list_append(_emotion_webcams->webcams, ew);
90    ew->in_list = EINA_TRUE;
91    if (fd >= 0) close(fd);
92    return EINA_TRUE;
93 
94  on_error:
95 #endif
96    INF("'%s' is not a webcam ['%s']", ew->name, strerror(errno));
97    emotion_webcam_destroy(ew);
98 #ifdef HAVE_V4L2
99    if (fd >= 0) close(fd);
100 #endif
101    return EINA_FALSE;
102 }
103 
104 static Emotion_Webcam *
_emotion_webcam_new(const char * syspath)105 _emotion_webcam_new(const char *syspath)
106 {
107    Emotion_Webcam *ew;
108    const char *device;
109    char *local;
110 
111    ew = calloc(1, sizeof(Emotion_Webcam));
112    if (!ew) return NULL;
113 
114    EINA_REFCOUNT_INIT(ew);
115    ew->syspath = eina_stringshare_ref(syspath);
116    ew->name = eeze_udev_syspath_get_sysattr(syspath, "name");
117 
118    device = eeze_udev_syspath_get_property(syspath, "DEVNAME");
119    local = alloca(eina_stringshare_strlen(device) + 8);
120    snprintf(local, eina_stringshare_strlen(device) + 8, "v4l2://%s", device);
121    ew->device = eina_stringshare_add(local);
122    eina_stringshare_del(device);
123    ew->filename = ew->device + 7;
124 
125    return ew;
126 }
127 
128 static void
_emotion_webcam_unref(Emotion_Webcam * ew)129 _emotion_webcam_unref(Emotion_Webcam *ew)
130 {
131    EINA_REFCOUNT_UNREF(ew)
132      {
133         if ((ew->in_list) && (_emotion_webcams))
134           {
135              _emotion_webcams->webcams =
136                eina_list_remove(_emotion_webcams->webcams, ew);
137              ew->in_list = EINA_FALSE;
138           }
139         emotion_webcam_destroy(ew);
140      }
141 }
142 
143 static void
_emotion_eeze_event_free(void * data EINA_UNUSED,void * ev)144 _emotion_eeze_event_free(void *data EINA_UNUSED, void *ev)
145 {
146    _emotion_webcam_unref(ev);
147 }
148 
149 static void
_emotion_webcam_ev_add(const char * syspath)150 _emotion_webcam_ev_add(const char *syspath)
151 {
152    Emotion_Webcam *ew = _emotion_webcam_new(syspath);
153    if (!ew) return;
154    if (!_emotion_check_device(ew)) return;
155    EINA_REFCOUNT_REF(ew);
156    ecore_event_add(EMOTION_WEBCAM_ADD, ew, _emotion_eeze_event_free, NULL);
157 }
158 
159 static Eina_Bool
_emotion_process_webcam(void * data)160 _emotion_process_webcam(void *data)
161 {
162    Emotion_Webcams *webcams = data;
163    const char *syspath;
164 
165    syspath = eina_list_data_get(webcams->check_list);
166    if (!syspath)
167      {
168         webcams->idler = NULL;
169         webcams->init = EINA_TRUE;
170         return EINA_FALSE;
171      }
172    webcams->check_list = eina_list_remove_list(webcams->check_list,
173                                                webcams->check_list);
174    _emotion_webcam_ev_add(syspath);
175    eina_stringshare_del(syspath);
176    return EINA_TRUE;
177 }
178 
179 static void
_emotion_webcam_remove_cb(void * data EINA_UNUSED,void * ev)180 _emotion_webcam_remove_cb(void *data EINA_UNUSED, void *ev)
181 {
182    _emotion_webcam_unref(ev);
183 }
184 
185 static void
_emotion_eeze_events(const char * syspath,Eeze_Udev_Event ev,void * data EINA_UNUSED,Eeze_Udev_Watch * watcher EINA_UNUSED)186 _emotion_eeze_events(const char *syspath, Eeze_Udev_Event ev,
187                      void *data EINA_UNUSED,
188                      Eeze_Udev_Watch *watcher EINA_UNUSED)
189 {
190    if (ev == EEZE_UDEV_EVENT_REMOVE)
191      {
192         Emotion_Webcam *ew;
193         Eina_List *l;
194 
195         EINA_LIST_FOREACH(_emotion_webcams->webcams, l, ew)
196           {
197              if (ew->syspath == syspath)
198                {
199                   if (ew->in_list)
200                     {
201                        _emotion_webcams->webcams =
202                          eina_list_remove_list(_emotion_webcams->webcams, l);
203                        ew->in_list = EINA_FALSE;
204                     }
205                   ecore_event_add(EMOTION_WEBCAM_DEL, ew,
206                                   _emotion_webcam_remove_cb, NULL);
207                   break;
208                }
209           }
210      }
211    else if (ev == EEZE_UDEV_EVENT_ADD)
212      {
213         _emotion_webcam_ev_add(syspath);
214      }
215    ecore_event_add(EMOTION_WEBCAM_UPDATE, NULL, NULL, NULL);
216 }
217 
218 #endif
219 
220 static void
_emotion_enumerate_all_webcams(void)221 _emotion_enumerate_all_webcams(void)
222 {
223 #ifdef HAVE_EEZE
224    Eina_List *devices;
225    if (_emotion_webcams->init) return;
226    devices = eeze_udev_find_by_type(EEZE_UDEV_TYPE_V4L, NULL);
227    _emotion_webcams->check_list = devices;
228    _emotion_webcams->idler = ecore_idler_add(_emotion_process_webcam,
229                                              _emotion_webcams);
230 #endif
231 }
232 
233 
emotion_webcam_init(void)234 Eina_Bool emotion_webcam_init(void)
235 {
236    EMOTION_WEBCAM_UPDATE = ecore_event_type_new();
237    EMOTION_WEBCAM_ADD = ecore_event_type_new();
238    EMOTION_WEBCAM_DEL = ecore_event_type_new();
239 
240    if (!_emotion_webcams)
241      {
242         _emotion_webcams = calloc(1, sizeof (Emotion_Webcams));
243         EINA_SAFETY_ON_NULL_RETURN_VAL(_emotion_webcams, EINA_FALSE);
244      }
245 
246 #ifdef HAVE_EEZE
247    eeze_init();
248    eeze_watcher = eeze_udev_watch_add
249      (EEZE_UDEV_TYPE_V4L, (EEZE_UDEV_EVENT_ADD | EEZE_UDEV_EVENT_REMOVE),
250      _emotion_eeze_events, NULL);
251 #endif
252 
253    return EINA_TRUE;
254 }
255 
256 void
emotion_webcam_shutdown(void)257 emotion_webcam_shutdown(void)
258 {
259    Emotion_Webcam *ew;
260    const char *syspath;
261 
262    ecore_event_type_flush(EMOTION_WEBCAM_UPDATE, EMOTION_WEBCAM_ADD,
263                           EMOTION_WEBCAM_DEL);
264 
265    if (_emotion_webcams->idler)
266      {
267         ecore_idler_del(_emotion_webcams->idler);
268         _emotion_webcams->idler = NULL;
269      }
270 
271    EINA_LIST_FREE(_emotion_webcams->check_list, syspath)
272      {
273         eina_stringshare_del(syspath);
274      }
275 
276    _emotion_webcams->init = EINA_FALSE;
277 
278    EINA_LIST_FREE(_emotion_webcams->webcams, ew)
279      {
280         ew->in_list = EINA_FALSE;
281         // There is currently no way to refcount from the outside, this helps
282         // but could lead to some issues
283         EINA_REFCOUNT_UNREF(ew)
284           {
285              emotion_webcam_destroy(ew);
286           }
287      }
288    free(_emotion_webcams);
289    _emotion_webcams = NULL;
290 
291 #ifdef HAVE_EEZE
292    eeze_udev_watch_del(eeze_watcher);
293    eeze_watcher = NULL;
294    eeze_shutdown();
295 #endif
296 }
297 
298 EAPI const Eina_List *
emotion_webcams_get(void)299 emotion_webcams_get(void)
300 {
301    EINA_SAFETY_ON_NULL_RETURN_VAL(_emotion_webcams, NULL);
302    _emotion_enumerate_all_webcams();
303    return _emotion_webcams->webcams;
304 }
305 
306 EAPI const char *
emotion_webcam_name_get(const Emotion_Webcam * ew)307 emotion_webcam_name_get(const Emotion_Webcam *ew)
308 {
309    EINA_SAFETY_ON_NULL_RETURN_VAL(ew, NULL);
310    return ew->name;
311 }
312 
313 EAPI const char *
emotion_webcam_device_get(const Emotion_Webcam * ew)314 emotion_webcam_device_get(const Emotion_Webcam *ew)
315 {
316    EINA_SAFETY_ON_NULL_RETURN_VAL(ew, NULL);
317    return ew->device;
318 }
319 
320 EAPI const char *
emotion_webcam_custom_get(const char * device EINA_UNUSED)321 emotion_webcam_custom_get(const char *device EINA_UNUSED)
322 {
323    return NULL;
324 }
325