comparison libvo/vo_bl.c @ 7326:ec3e58120e2a

extensible blinkenlights driver, can currently be used for the Arcade http://www.blinkenlights.de/arcade
author rik
date Sun, 08 Sep 2002 22:41:53 +0000
parents
children 5b39e79af5fe
comparison
equal deleted inserted replaced
7325:45228f938e90 7326:ec3e58120e2a
1 /*
2 * vo_bl.c - playback using the Blinkenlights UPD protocol (and to files)
3 *
4 * UDP socket handling copied from bsender.c part of blib-0.6:
5 * http://sven.gimp.org/blinkenlights/
6 * Copyright (c) 2001-2001 The Blinkenlights Crew:
7 * Sven Neumann <sven@gimp.org>
8 * Michael Natterer <mitch@gimp.org>
9 * Daniel Mack <daniel@yoobay.net>
10 * (these portions are licensed under GNU GPL v2 or "(at your option)
11 * any later version")
12 *
13 * Other stuff: Copyright (C) Rik Snel 2002, License GNU GPL v2
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/time.h>
26 #include <sys/mman.h>
27 #include <sys/ioctl.h>
28 #include <netinet/in.h>
29 #include <linux/types.h>
30
31 #include "config.h"
32
33 #include "video_out.h"
34 #include "video_out_internal.h"
35 #include "../mp_msg.h"
36 #include "../cfgparser.h"
37 #include "fastmemcpy.h"
38
39 LIBVO_EXTERN (bl)
40
41 static vo_info_t vo_info =
42 {
43 "Blinkenlights driver: http://www.blinkenlights.de",
44 "bl",
45 "Rik Snel <snel@phys.uu.nl>",
46 ""
47 };
48
49 /* General variables */
50
51 static unsigned char *image = NULL;
52 static unsigned char *tmp = NULL;
53 static int framenum, yoff, stride;
54 static char *bl_subdevice = NULL;
55 static int prevpts = -1;
56
57 typedef struct {
58 char *name; /* filename */
59 FILE *fp;
60 } bl_file_t;
61
62 typedef struct {
63 char *name; /* hostname */
64 int port;
65 int fd; /* file descriptor */
66 } bl_host_t;
67
68 typedef struct {
69 char *name;
70 int img_format;
71
72 int channels;
73 int width;
74 int height;
75 int bpc; /* bits per component: bpc = 3, channels = 3 => bpp = 24*/
76
77 /* file output functions */
78 int (*init_file)(bl_file_t *file);
79 void (*write_frame)(bl_file_t *file, unsigned char *i, int duration);
80 void (*close_file)(bl_file_t *file);
81
82 /* network output functions */
83 int (*init_connection)(bl_host_t *host);
84 void (*send_frame)(bl_host_t *host);
85 void (*close_connection)(bl_host_t *host);
86 } bl_properties_t;
87
88 static bl_properties_t *bl = NULL;
89
90 /* arbitrary limit because I am too lazy to do proper memory management */
91 #define BL_MAX_FILES 16
92 #define BL_MAX_HOSTS 16
93 static bl_file_t bl_files[BL_MAX_FILES];
94 static bl_host_t bl_hosts[BL_MAX_HOSTS];
95 static int no_bl_files = 0;
96 static int no_bl_hosts = 0;
97
98 typedef struct {
99 uint32_t magic;
100 uint16_t height;
101 uint16_t width;
102 uint16_t channels;
103 uint16_t maxval;
104 unsigned char data[0];
105 } bl_packet_t;
106
107 static bl_packet_t *bl_packet = NULL;
108 static int bl_size;
109
110 /* bml output functions */
111 static int bml_init(bl_file_t *f) {
112 f->fp = fopen(f->name, "w");
113 if (!f->fp) {
114 mp_msg(MSGT_VO, MSGL_ERR, "bl: error opening %s\n", f->name);
115 return 1;
116 }
117 fprintf(f->fp,
118 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
119 "<blm width=\"%d\" height=\"%d\" bits=\"%d\" channels=\"%d\">\n"
120 " <header>\n"
121 " <title>Movie autogenerated by MPlayer</title>\n"
122 " <url>http://www.mplayerhq.hu</url>\n"
123 " </header>\n", bl->width, bl->height, bl->bpc, bl->channels);
124 return 0;
125 }
126
127 static void bml_write_frame(bl_file_t *f, unsigned char *i, int duration) {
128 int j, k;
129 fprintf(f->fp, " <frame duration=\"%d\">\n", duration);
130 for (j = 0; j < bl->height; j++) {
131 fprintf(f->fp, " <row>");
132 for (k = 0; k < bl->width; k++)
133 fprintf(f->fp, "%02x", *(i + j * bl->width + k));
134 fprintf(f->fp, "</row>\n");
135 }
136 fprintf(f->fp, " </frame>\n");
137 }
138
139 static void bml_close(bl_file_t *f) {
140 fprintf(f->fp, "</blm>\n");
141 fclose(f->fp);
142 }
143
144 /* Blinkenlights UDP protocol */
145 static int udp_init(bl_host_t *h) {
146 struct sockaddr_in addr;
147 struct hostent *dest;
148
149 dest = gethostbyname(h->name);
150 if (!dest) {
151 mp_msg(MSGT_VO, MSGL_ERR,
152 "unable to resolve host %s\n", h->name);
153 return 1;
154 }
155
156 h->fd = -1;
157 addr.sin_family = AF_INET;
158 addr.sin_port = htons(h->port);
159
160 memcpy(&addr.sin_addr.s_addr, dest->h_addr_list[0], dest->h_length);
161
162 h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
163 if (h->fd < 0) {
164 mp_msg(MSGT_VO, MSGL_ERR,
165 "couldn't create socket for %s\n", h->name);
166 return 1;
167 }
168 if (connect(h->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
169 mp_msg(MSGT_VO, MSGL_ERR, "couldn't connect socket for %s\n",
170 h->name);
171 close(h->fd);
172 return 1;
173 }
174 return 0;
175 }
176
177 static void udp_send(bl_host_t *h) {
178 if (write(h->fd, bl_packet, bl_size) != bl_size)
179 mp_msg(MSGT_VO, MSGL_ERR, "unable to send to %s\n", h->name);
180 }
181
182 static void udp_close(bl_host_t *h) {
183 close(h->fd);
184 }
185
186 #define NO_BLS 2
187
188 /* currently only arcade is supported, hdl can be supported
189 * in principle and future projects can be supported if their
190 * parameters become known */
191 static bl_properties_t bls[NO_BLS] = {
192 { "hdl", IMGFMT_BGR1, 1, 18, 8, 1,
193 NULL, NULL, NULL, NULL, NULL, NULL },
194 { "arcade", IMGFMT_YV12, 1, 26, 20, 8,
195 &bml_init, &bml_write_frame, &bml_close,
196 &udp_init, &udp_send, &udp_close } };
197
198 static uint32_t config(uint32_t width, uint32_t height, uint32_t d_width,
199 uint32_t d_height, uint32_t fullscreen, char *title, uint32_t format)
200 {
201 framenum = 0;
202 if (format != IMGFMT_YV12) {
203 mp_msg(MSGT_VO, MSGL_ERR, "vo_bl called with wrong format");
204 return 1;
205 }
206 if (width > bl->width) {
207 mp_msg(MSGT_VO, MSGL_ERR, "bl: width of movie too large %d > %d\n", width, bl->width);
208 return 1;
209 }
210 if (height > bl->height) {
211 mp_msg(MSGT_VO, MSGL_ERR, "bl: height of movie too large %d > %d\n", height, bl->height);
212 return 1;
213 }
214 if (!image) {
215 mp_msg(MSGT_VO, MSGL_ERR, "bl: image should be initialized, internal error\n");
216 return 1;
217 }
218 memset(image, 0, bl->width*bl->height*3); /* blank the image */
219 mp_msg(MSGT_VO, MSGL_V, "vo_config bl called\n");
220 return 0;
221 }
222
223 static const vo_info_t* get_info(void) {
224 return &vo_info;
225 }
226
227 static void draw_osd(void) {
228 }
229
230 static void flip_page (void) {
231 int i;
232
233 if (prevpts >= 0) for (i = 0; i < no_bl_files; i++)
234 bl->write_frame(&bl_files[i], tmp, (vo_pts - prevpts)/90);
235 memcpy(tmp, image, bl->width*bl->height*bl->channels);
236 prevpts = vo_pts;
237
238 for (i = 0; i < no_bl_hosts; i++) bl->send_frame(&bl_hosts[i]);
239
240
241 framenum++;
242 return;
243 }
244
245 static uint32_t draw_frame(uint8_t * src[]) {
246 int i, j;
247 char *source, *dest;
248 //printf("draw frame called\n");
249 #if 0
250 zr_info_t *zr = &zr_info[j];
251 geo_t *g = &zr->g;
252 source = src[0] + 2*g->yoff*zr->vdec*zr->stride + 2*g->xoff;
253 dest = zr->image + 2*zr->off_y;
254 for (i = 0; i < g->height/zr->vdec; i++) {
255 memcpy(dest, source, zr->image_width*2);
256 dest += 2*zr->image_width;
257 source += zr->vdec*zr->stride;
258 }
259 #endif
260 return 0;
261 }
262
263 static uint32_t query_format(uint32_t format) {
264 if (format == bl->img_format)
265 return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW;
266 return 0;
267 }
268
269 static void uninit(void) {
270 int i;
271 mp_msg(MSGT_VO, MSGL_V, "bl: uninit called\n");
272 free(bl_packet);
273 bl_packet = NULL;
274 free(bl_subdevice);
275 bl_subdevice = NULL;
276 for (i = 0; i < no_bl_files; i++) bl->close_file(&bl_files[i]);
277 for (i = 0; i < no_bl_hosts; i++) bl->close_connection(&bl_hosts[i]);
278 no_bl_files = 0;
279 no_bl_hosts = 0;
280 bl = NULL;
281 }
282
283 static void check_events(void) {
284 }
285
286 static uint32_t draw_slice(uint8_t *srcimg[], int stride[],
287 int wf, int hf, int xf, int yf) {
288 int i, j, w, h, x, y;
289 uint8_t *dst;
290 uint8_t *src=srcimg[0];
291 uint8_t *src1=srcimg[1];
292 uint8_t *src2=srcimg[2];
293 w = wf; h = hf; x = xf; y = yf;
294 dst=image; /* + zr->off_y + zr->image_width*(y/zr->vdec)+x;*/
295 // copy Y:
296 for (i = 0; i < h; i++) {
297 memcpy(dst,src,w);
298 dst+=bl->width;
299 src+=stride[0];
300
301 }
302 return 0;
303 }
304
305 static uint32_t preinit(const char *arg) {
306 char *p, *q;
307 int end = 0, i;
308 if (!arg || strlen(arg) == 0) {
309 mp_msg(MSGT_VO, MSGL_ERR, "bl: subdevice must be given, example: -vo bl:arcade:host=localhost\n");
310 return 1;
311 }
312
313 bl_subdevice = malloc(strlen(arg) + 1);
314 if (!bl_subdevice) {
315 mp_msg(MSGT_VO, MSGL_ERR, "bl: out of memory error\n");
316 return 1;
317 }
318 p = bl_subdevice;
319 strcpy(p, arg);
320 mp_msg(MSGT_VO, MSGL_V, "bl: preinit called with %s\n", arg);
321 if (strncmp(p, "arcade", 6)) {
322 mp_msg(MSGT_VO, MSGL_ERR, "bl: subdevice must start with arcade, this is the only supported output format\nat the moment, i.e. -vo bl:arcade:host=localhost\n");
323 return 1;
324 }
325 bl = &bls[1];
326 p += 6;
327 if (*p == '\0') {
328 no_bl_hosts = 1;
329 bl_hosts[0].name = "localhost";
330 bl_hosts[0].port = 2323;
331 mp_msg(MSGT_VO, MSGL_V, "bl: no hosts/files specified, using localhost:2323\n");
332 end = 1;
333 } else if (*p != ':') {
334 mp_msg(MSGT_VO, MSGL_ERR, "bl: syntax error in subdevice\n");
335 return 1;
336 }
337 p++;
338
339 while (!end) {
340 q = p + 5;
341 if (!strncmp(p, "file=", 5)) {
342 if (no_bl_files == BL_MAX_FILES) {
343 mp_msg(MSGT_VO, MSGL_ERR, "bl: maximum number of hosts reached (%d)\n", BL_MAX_FILES);
344 return 1;
345 }
346 p += 5;
347 while (*q != ',' && *q != '\0') q++;
348 if (*q == '\0') end = 1;
349 *q = '\0';
350 bl_files[no_bl_files].name = p;
351 mp_msg(MSGT_VO, MSGL_V, "blfile[%d]: %s\n",
352 no_bl_files, p);
353 no_bl_files++;
354 } else if (!strncmp(p, "host=", 5)) {
355 if (no_bl_hosts == BL_MAX_HOSTS) {
356 mp_msg(MSGT_VO, MSGL_ERR, "bl: maximum number of hosts reached (%d)\n", BL_MAX_HOSTS);
357 return 1;
358 }
359 p += 5;
360 while (*q != ',' && *q != '\0' && *q != ':') q++;
361 if (*q == ':') {
362 *q++ = '\0';
363 bl_hosts[no_bl_hosts].name = p;
364 bl_hosts[no_bl_hosts].port = atoi(q);
365 while (*q != ',' && *q != '\0') q++;
366 if (*q == '\0') end = 1;
367 } else {
368 /* use default port */
369 if (*q == '\0') end = 1;
370 *q = '\0';
371 bl_hosts[no_bl_hosts].name = p;
372 bl_hosts[no_bl_hosts].port = 2323;
373 }
374 mp_msg(MSGT_VO, MSGL_V,
375 "blhost[%d]: %s:%d\n",
376 no_bl_hosts, p,
377 bl_hosts[no_bl_hosts].port);
378 no_bl_hosts++;
379 } else {
380 mp_msg(MSGT_VO, MSGL_ERR, "bl: syntax error in entry %d in subdevice %s, should be a comma seperated\nlist of host=name:port and file=foo.bml\n", no_bl_hosts, no_bl_files, arg);
381 return 1;
382 }
383 p = ++q;
384 }
385
386 bl_size = bl->width*bl->height*bl->channels + 12;
387 /* enough space for RGB 24 bit + header */
388 bl_packet = malloc(bl->width*bl->height*3+12);
389 image = ((unsigned char*)bl_packet + 12);
390 tmp = malloc(bl->width*bl->height*bl->channels);
391
392 if (!bl_packet || !tmp) {
393 mp_msg(MSGT_VO, MSGL_ERR, "bl: out of memory error\n");
394 return 1;
395 }
396 bl_packet->magic = htonl(0x23542666);
397 bl_packet->width = htons(bl->width);
398 bl_packet->height = htons(bl->height);
399 bl_packet->channels = htons(bl->channels);
400 bl_packet->maxval = htons(2<<bl->bpc - 1);
401
402 /* open all files */
403 for (i = 0; i < no_bl_files; i++)
404 if (bl->init_file(&bl_files[i])) return 1;
405
406 /* open all sockets */
407 for (i = 0; i < no_bl_hosts; i++)
408 if (bl->init_connection(&bl_hosts[i])) return 1;
409
410
411 return 0;
412 }
413
414 static uint32_t control(uint32_t request, void *data, ...) {
415 switch (request) {
416 case VOCTRL_QUERY_FORMAT:
417 return query_format(*((uint32_t*)data));
418 }
419 return VO_NOTIMPL;
420 }