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