1 // LiVES - vloopback playback engine
2 // (c) G. Finch 2010 - 2019 <salsaman+lives@gmail.com>
3 // released under the GNU GPL 3 or later
4 // see file COPYING or www.gnu.org for details
5 
6 #include "videoplugin.h"
7 
8 #include <stdio.h>
9 
10 /////////////////////////////////////////////////////////////////
11 
12 static char plugin_version[64] = "LiVES vloopback output client 1.0.3";
13 static int palette_list[3];
14 static int clampings[2];
15 static int mypalette;
16 
17 //////////////////////////////////////////////////////////////////
18 
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <dirent.h>
24 #include <unistd.h>
25 
26 #if v4l1_INCFILE == 2
27 #include <libv4l1-videodev.h>
28 #else
29 #if v4l1_INCFILE == 1
30 #include <linux/videodev.h>
31 #endif
32 #endif
33 
34 #include <sys/ioctl.h>
35 
36 static struct video_window x_vidwin;
37 static struct video_picture x_vidpic;
38 
39 static int vdevfd;
40 
41 static char *vdevname;
42 
43 
44 //////////////////////////////////////////////
45 
file_filter(const struct dirent * a)46 static int file_filter(const struct dirent *a) {
47   int match = 0;
48 
49   // match: 'videoXY' where X = {0..9} and Y = {0..9}
50   if (!strncmp(a->d_name, "video", 5)) {
51     if (strlen(a->d_name) > 5) {
52       if ((a->d_name[5] >= '0') && (a->d_name[5] <= '9')) {
53         // match
54         // the 'X'
55         match = 1;
56       }
57 
58       if (strlen(a->d_name) > 6) {
59         match = 0;
60 
61         if ((a->d_name[6] >= '0') && (a->d_name[6] <= '9')) {
62           match = 1;
63         }
64       }
65 
66       if (strlen(a->d_name) > 7) {
67         match = 0;
68       }
69     }
70   }
71 
72   return match;
73 }
74 
75 
76 #define MAX_DEVICES 65
77 
get_vloopback_devices(void)78 static char **get_vloopback_devices(void) {
79   struct dirent **namelist;
80   struct video_capability v4lcap;
81   char **devnames = malloc(MAX_DEVICES * sizeof(char *));
82 
83   char devname[PATH_MAX];
84   int i, n, fd, ndevices = 0;
85 
86   for (i = 0; i < MAX_DEVICES; devnames[i++] = NULL);
87 
88   n = scandir("/dev", &namelist, file_filter, alphasort);
89   if (n < 0) return devnames;
90 
91   for (i = 0; i < n && ndevices < MAX_DEVICES - 1; i++) {
92     sprintf(devname, "/dev/%s", namelist[i]->d_name);
93 
94     if ((fd = open(devname, O_RDONLY | O_NONBLOCK)) == -1) {
95       // could not open device
96       continue;
97     }
98 
99     if (ioctl(fd, VIDIOCGCAP, &v4lcap) < 0) {
100       // not a video device
101       close(fd);
102       continue;
103     }
104 
105     // is it vloopback ?
106     if (strstr(v4lcap.name, "loopback") == NULL) continue;
107 
108     if ((v4lcap.type & VID_TYPE_CAPTURE)) {
109       // is an output device
110       close(fd);
111       continue;
112     }
113 
114     close(fd);
115     devnames[ndevices++] = strdup(devname);
116     //fprintf(stderr,"got %s\n",devname);
117   }
118   devnames[ndevices] = NULL;
119 
120   for (i = 0; i < n; free(namelist[i++]));
121   free(namelist);
122 
123   return devnames;
124 }
125 
126 
127 ///////////////////////////////////////////////////
128 
module_check_init(void)129 const char *module_check_init(void) {
130   char **vdevs = get_vloopback_devices();
131   int i = 0;
132 
133   if (vdevs[0] == NULL) {
134     free(vdevs);
135     return "No vloopback devices were found\nTry: sudo modprobe vloopback\n";
136   }
137 
138   while (vdevs[i] != NULL) free(vdevs[i++]);
139   free(vdevs);
140 
141   return NULL;
142 }
143 
144 
version(void)145 const char *version(void) {
146   return plugin_version;
147 }
148 
149 
get_description(void)150 const char *get_description(void) {
151   return "The vloopback playback plugin makes LiVES appear as a video device in /dev.\n";
152 }
153 
154 
get_capabilities(int palette)155 uint64_t get_capabilities(int palette) {
156   return 0;
157 }
158 
159 
160 const char rfx[32768];
161 
get_init_rfx(int intention)162 const char *get_init_rfx(int intention) {
163   char **vdevs = get_vloopback_devices();
164   char devstr[30000];
165   size_t slen = 0;
166   int i = 0;
167 
168   if (vdevs[0] == NULL) {
169     free(vdevs);
170     return "No vloopback devices were found\nTry: sudo modprobe vloopback\n";
171   }
172 
173   memset(devstr, 0, 1);
174 
175   while (vdevs[i] != NULL) {
176     snprintf(devstr + slen, 30000 - slen, "%s|", vdevs[i]);
177     slen += strlen(vdevs[i]) + 1;
178     free(vdevs[i++]);
179   }
180   free(vdevs);
181 
182   snprintf((char *)rfx, 32768, "%s%s%s",
183            "<define>\\n\
184 |1.7\\n\
185 </define>\\n\
186 <language_code>\\n\
187 0xF0\\n\
188 </language_code>\\n\
189 <params> \\n\
190 vdevname|Video _device|string_list|0|",
191            devstr,
192            "\\n\
193 </params> \\n\
194 <param_window> \\n\
195 </param_window> \\n\
196 <onchange> \\n\
197 </onchange> \\n\
198 "
199           );
200 
201   return rfx;
202 }
203 
204 
get_palette_list(void)205 const int *get_palette_list(void) {
206   palette_list[0] = WEED_PALETTE_UYVY;
207   palette_list[1] = WEED_PALETTE_RGB24;
208   palette_list[2] = WEED_PALETTE_END;
209   return palette_list;
210 }
211 
212 
set_palette(int palette)213 boolean set_palette(int palette) {
214   if (palette == WEED_PALETTE_UYVY) {
215     mypalette = palette;
216     return TRUE;
217   }
218   if (palette == WEED_PALETTE_RGB24) {
219     mypalette = palette;
220     return TRUE;
221   }
222   // invalid palette
223   return FALSE;
224 }
225 
226 
get_yuv_palette_clamping(int palette)227 const int *get_yuv_palette_clamping(int palette) {
228   if (palette == WEED_PALETTE_RGB24) clampings[0] = -1;
229   else {
230     clampings[0] = WEED_YUV_CLAMPING_CLAMPED;
231     clampings[1] = -1;
232   }
233   return clampings;
234 }
235 
236 
init_screen(int width,int height,boolean fullscreen,uint64_t window_id,int argc,char ** argv)237 boolean init_screen(int width, int height, boolean fullscreen, uint64_t window_id, int argc, char **argv) {
238   char **vdevs;
239   int i = 0, idx = 0;
240 
241   vdevfd = -1;
242 
243   if (argc > 0) idx = atoi(argv[0]);
244 
245   vdevs = get_vloopback_devices();
246   if (vdevs[idx]) {
247     vdevname = strdup(vdevs[idx]);
248   } else vdevname = NULL;
249 
250   while (vdevs[i]) free(vdevs[i++]);
251   free(vdevs);
252 
253   if (!vdevname) return FALSE;
254 
255   vdevfd = open(vdevname, O_WRONLY);
256 
257   if (vdevfd == -1) {
258     fprintf(stderr, "vloopback output: cannot open %s %s\n", vdevname, strerror(errno));
259     return FALSE;
260   }
261 
262   if (ioctl(vdevfd, VIDIOCGPICT, &x_vidpic) == -1) {
263     fprintf(stderr, "vloopback output: cannot get palette for %s\n", vdevname);
264     return FALSE;
265   }
266 
267   if (mypalette == WEED_PALETTE_RGB24) x_vidpic.palette = VIDEO_PALETTE_RGB24;
268   else if (mypalette == WEED_PALETTE_UYVY) x_vidpic.palette = VIDEO_PALETTE_UYVY;
269 
270   if (ioctl(vdevfd, VIDIOCSPICT, &x_vidpic) == -1) {
271     fprintf(stderr, "vloopback output: cannot set palette for %s\n", vdevname);
272     return FALSE;
273   }
274 
275   if (ioctl(vdevfd, VIDIOCGWIN, &x_vidwin) == -1) {
276     fprintf(stderr, "vloopback output: cannot get dimensions for %s\n", vdevname);
277     return FALSE;
278   }
279 
280   x_vidwin.width = width;
281   x_vidwin.height = height;
282 
283   if (ioctl(vdevfd, VIDIOCSWIN, &x_vidwin) == -1) {
284     fprintf(stderr, "vloopback output: cannot set dimensions for %s\n", vdevname);
285     return FALSE;
286   }
287   return TRUE;
288 }
289 
290 
render_frame(int hsize,int vsize,int64_t tc,void ** pixel_data,void ** rd,void ** pp)291 boolean render_frame(int hsize, int vsize, int64_t tc, void **pixel_data, void **rd, void **pp) {
292   // hsize and vsize are in [macro]pixels (n-byte)
293   size_t frame_size, bytes;
294 
295   if (mypalette == WEED_PALETTE_UYVY) frame_size = hsize * vsize * 4;
296   else frame_size = hsize * vsize * 3;
297 
298   bytes = write(vdevfd, pixel_data[0], frame_size);
299 
300   if (bytes != frame_size) {
301     fprintf(stderr, "Error writing frame to %s\n", vdevname);
302     return FALSE;
303   }
304 
305   return TRUE;
306 }
307 
308 
exit_screen(int16_t mouse_x,int16_t mouse_y)309 void exit_screen(int16_t mouse_x, int16_t mouse_y) {
310   int xval = 0;
311   if (vdevfd != -1) xval = close(vdevfd);
312   if (vdevname != NULL) free(vdevname);
313   xval = xval;
314 }
315