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