2 * vo_bl.c - playback using the Blinkenlights UPD protocol (and to files)
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)
13 * Other stuff: Copyright (C) Rik Snel 2002, License GNU GPL v2 or later
15 * patch from Stefan Schuermans <1stein@schuermans.info>:
16 * - correction of "maxval" in Blinkenlights UDP protcol
17 * - new scheme for new HDL
18 * - new scheme for grayscale in arbitrary size
28 #include <sys/types.h>
30 #ifdef HAVE_SYS_MMAN_H
33 #include <sys/ioctl.h>
38 #define closesocket close
40 #include <sys/socket.h>
41 #include <netinet/in.h>
46 #include "video_out.h"
47 #include "video_out_internal.h"
50 #include "fastmemcpy.h"
52 static const vo_info_t info
=
54 "Blinkenlights driver: http://www.blinkenlights.de",
56 "Rik Snel <snel@phys.uu.nl>",
60 const LIBVO_EXTERN (bl
)
62 /* General variables */
64 static unsigned char *image
= NULL
;
65 static unsigned char *tmp
= NULL
;
67 static char *bl_subdevice
= NULL
;
68 static int prevpts
= -1;
71 char *name
; /* filename */
73 int header_written
; /* if header was written already */
77 char *name
; /* hostname */
79 int fd
; /* file descriptor */
89 int bpc
; /* bits per component: bpc = 3, channels = 3 => bpp = 24*/
91 /* file output functions */
92 int (*init_file
)(bl_file_t
*file
);
93 void (*write_frame
)(bl_file_t
*file
, unsigned char *i
, int duration
);
94 void (*close_file
)(bl_file_t
*file
);
96 /* network output functions */
97 int (*init_connection
)(bl_host_t
*host
);
98 void (*send_frame
)(bl_host_t
*host
);
99 void (*close_connection
)(bl_host_t
*host
);
102 static bl_properties_t
*bl
= NULL
;
104 /* arbitrary limit because I am too lazy to do proper memory management */
105 #define BL_MAX_FILES 16
106 #define BL_MAX_HOSTS 16
107 static bl_file_t bl_files
[BL_MAX_FILES
];
108 static bl_host_t bl_hosts
[BL_MAX_HOSTS
];
109 static int no_bl_files
= 0;
110 static int no_bl_hosts
= 0;
118 unsigned char data
[0];
121 static bl_packet_t
*bl_packet
= NULL
;
124 /* bml output functions */
125 static int bml_init(bl_file_t
*f
) {
126 f
->fp
= fopen(f
->name
, "w");
128 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: error opening %s\n", f
->name
);
131 f
->header_written
= 0;
135 static void bml_write_frame(bl_file_t
*f
, unsigned char *i
, int duration
) {
137 if( ! f
->header_written
)
140 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
141 "<blm width=\"%d\" height=\"%d\" bits=\"%d\" channels=\"%d\">\n"
143 " <title>Movie autogenerated by MPlayer</title>\n"
144 " <url>http://www.mplayerhq.hu</url>\n"
145 " </header>\n", bl
->width
, bl
->height
, bl
->bpc
, bl
->channels
);
146 f
->header_written
= 1;
148 fprintf(f
->fp
, " <frame duration=\"%d\">\n", duration
);
149 for (j
= 0; j
< bl
->height
; j
++) {
150 fprintf(f
->fp
, " <row>");
151 for (k
= 0; k
< bl
->width
; k
++)
152 fprintf(f
->fp
, "%02x", *(i
+ j
* bl
->width
+ k
));
153 fprintf(f
->fp
, "</row>\n");
155 fprintf(f
->fp
, " </frame>\n");
158 static void bml_close(bl_file_t
*f
) {
159 fprintf(f
->fp
, "</blm>\n");
163 /* Blinkenlights UDP protocol */
164 static int udp_init(bl_host_t
*h
) {
165 struct sockaddr_in addr
;
166 struct hostent
*dest
;
168 dest
= gethostbyname(h
->name
);
170 mp_msg(MSGT_VO
, MSGL_ERR
,
171 "unable to resolve host %s\n", h
->name
);
176 addr
.sin_family
= AF_INET
;
177 addr
.sin_port
= htons(h
->port
);
179 memcpy(&addr
.sin_addr
.s_addr
, dest
->h_addr_list
[0], dest
->h_length
);
181 h
->fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
183 mp_msg(MSGT_VO
, MSGL_ERR
,
184 "couldn't create socket for %s\n", h
->name
);
187 if (connect(h
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
188 mp_msg(MSGT_VO
, MSGL_ERR
, "couldn't connect socket for %s\n",
196 static void udp_send(bl_host_t
*h
) {
197 if (send(h
->fd
, bl_packet
, bl_size
, 0) != bl_size
)
198 mp_msg(MSGT_VO
, MSGL_ERR
, "unable to send to %s\n", h
->name
);
201 static void udp_close(bl_host_t
*h
) {
207 static bl_properties_t bls
[NO_BLS
] = {
208 { "hdl", IMGFMT_YV12
, 1, 18, 8, 8,
209 &bml_init
, &bml_write_frame
, &bml_close
,
210 &udp_init
, &udp_send
, &udp_close
},
211 { "arcade", IMGFMT_YV12
, 1, 26, 20, 8,
212 &bml_init
, &bml_write_frame
, &bml_close
,
213 &udp_init
, &udp_send
, &udp_close
},
214 { "grayscale", IMGFMT_YV12
, 1, -1, -1, 8, /* use width and height of movie */
215 &bml_init
, &bml_write_frame
, &bml_close
,
216 &udp_init
, &udp_send
, &udp_close
} };
218 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
219 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
223 /* adapt size of Blinkenlights UDP stream to size of movie */
224 if (bl
->width
< 0 || bl
->height
< 0) {
225 if (bl
->width
< 0) { /* use width of movie */
227 bl_packet
->width
= htons(bl
->width
);
229 if (bl
->height
< 0) { /* use height of movie */
231 bl_packet
->height
= htons(bl
->height
);
233 /* check for maximum size of UDP packet */
234 if (12 + bl
->width
*bl
->height
*bl
->channels
> 65507) {
235 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: %dx%d-%d does not fit into an UDP packet\n",
236 bl
->width
, bl
->height
, bl
->channels
);
239 /* resize frame and tmp buffers */
240 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
241 ptr
= realloc(bl_packet
, 12 + bl
->width
*bl
->height
*3); /* space for header and image data */
243 bl_packet
= (bl_packet_t
*)ptr
;
245 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
248 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
249 ptr
= realloc(tmp
, bl
->width
*bl
->height
*3); /* space for image data only */
251 tmp
= (unsigned char*)ptr
;
253 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
259 if (format
!= IMGFMT_YV12
) {
260 mp_msg(MSGT_VO
, MSGL_ERR
, "vo_bl called with wrong format");
263 if (width
> bl
->width
) {
264 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: width of movie too large %d > %d\n", width
, bl
->width
);
267 if (height
> bl
->height
) {
268 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: height of movie too large %d > %d\n", height
, bl
->height
);
272 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: image should be initialized, internal error\n");
275 memset(image
, 0, bl
->width
*bl
->height
*3); /* blank the image */
276 mp_msg(MSGT_VO
, MSGL_V
, "vo_config bl called\n");
280 static void draw_osd(void) {
283 static void flip_page (void) {
286 if (prevpts
>= 0) for (i
= 0; i
< no_bl_files
; i
++)
287 bl
->write_frame(&bl_files
[i
], tmp
, (vo_pts
- prevpts
)/90);
288 fast_memcpy(tmp
, image
, bl
->width
*bl
->height
*bl
->channels
);
291 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->send_frame(&bl_hosts
[i
]);
298 static int draw_frame(uint8_t * src
[]) {
302 static int query_format(uint32_t format
) {
303 if (format
== bl
->img_format
)
304 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
308 static void uninit(void) {
310 mp_msg(MSGT_VO
, MSGL_V
, "bl: uninit called\n");
315 for (i
= 0; i
< no_bl_files
; i
++) bl
->close_file(&bl_files
[i
]);
316 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->close_connection(&bl_hosts
[i
]);
322 static void check_events(void) {
325 static int draw_slice(uint8_t *srcimg
[], int stride
[],
326 int wf
, int hf
, int xf
, int yf
) {
329 uint8_t *src
=srcimg
[0];
330 w
= wf
; h
= hf
; x
= xf
; y
= yf
;
331 dst
=image
; /* + zr->off_y + zr->image_width*(y/zr->vdec)+x;*/
333 for (i
= 0; i
< h
; i
++) {
334 fast_memcpy(dst
,src
,w
);
342 static int preinit(const char *arg
) {
346 if (!arg
|| strlen(arg
) == 0) {
347 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must be given, example: -vo bl:arcade:host=localhost:2323\n");
351 bl_subdevice
= malloc(strlen(arg
) + 1);
353 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
358 mp_msg(MSGT_VO
, MSGL_V
, "bl: preinit called with %s\n", arg
);
359 for (i
= 0; i
< NO_BLS
; i
++) {
360 if (!strncmp(p
, bls
[i
].name
, strlen(bls
[i
].name
)))
365 for (i
= 0; i
< NO_BLS
; i
++)
366 if (strlen( txt
) + 4 + strlen( bls
[i
].name
) + 1 < sizeof(txt
))
367 sprintf( txt
+ strlen( txt
), "%s%s",
368 txt
[0] == 0 ? "" : i
== NO_BLS
- 1 ? " or " : ", ", bls
[i
].name
);
369 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must start with %s\nbl: i.e. -vo bl:arcade:host=localhost:2323\n", txt
);
373 p
+= strlen(bls
[i
].name
);
376 bl_hosts
[0].name
= "localhost";
377 bl_hosts
[0].port
= 2323;
378 mp_msg(MSGT_VO
, MSGL_V
, "bl: no hosts/files specified, using localhost:2323\n");
380 } else if (*p
!= ':') {
381 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: syntax error in subdevice\n");
388 if (!strncmp(p
, "file=", 5)) {
389 if (no_bl_files
== BL_MAX_FILES
) {
390 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of files reached (%d)\n", BL_MAX_FILES
);
394 while (*q
!= ',' && *q
!= '\0') q
++;
395 if (*q
== '\0') end
= 1;
397 bl_files
[no_bl_files
].name
= p
;
398 mp_msg(MSGT_VO
, MSGL_V
, "blfile[%d]: %s\n",
401 } else if (!strncmp(p
, "host=", 5)) {
402 if (no_bl_hosts
== BL_MAX_HOSTS
) {
403 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of hosts reached (%d)\n", BL_MAX_HOSTS
);
407 while (*q
!= ',' && *q
!= '\0' && *q
!= ':') q
++;
410 bl_hosts
[no_bl_hosts
].name
= p
;
411 bl_hosts
[no_bl_hosts
].port
= atoi(q
);
412 while (*q
!= ',' && *q
!= '\0') q
++;
413 if (*q
== '\0') end
= 1;
415 /* use default port */
416 if (*q
== '\0') end
= 1;
418 bl_hosts
[no_bl_hosts
].name
= p
;
419 bl_hosts
[no_bl_hosts
].port
= 2323;
421 mp_msg(MSGT_VO
, MSGL_V
,
422 "blhost[%d]: %s:%d\n",
424 bl_hosts
[no_bl_hosts
].port
);
427 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: syntax error in entry %d in subdevice %s, should be a comma separated\nlist of host=name:port and file=foo.bml\n", no_bl_hosts
+ no_bl_files
, arg
);
433 if (bl
->width
>= 0 && bl
->height
>= 0) { /* size already known */
434 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
435 bl_packet
= malloc(12 + bl
->width
*bl
->height
*3); /* space for header and image data */
436 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
437 tmp
= malloc(bl
->width
*bl
->height
*3); /* space for image data only */
439 else { /* size unknown yet */
441 bl_packet
= malloc(12 + 3); /* space for header and a pixel */
442 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
443 tmp
= malloc(3); /* space for a pixel only */
446 if (!bl_packet
|| !tmp
) {
447 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
450 bl_packet
->magic
= htonl(0x23542666);
451 bl_packet
->width
= htons(bl
->width
);
452 bl_packet
->height
= htons(bl
->height
);
453 bl_packet
->channels
= htons(bl
->channels
);
454 bl_packet
->maxval
= htons((1 << bl
->bpc
) - 1);
457 for (i
= 0; i
< no_bl_files
; i
++)
458 if (bl
->init_file(&bl_files
[i
])) return 1;
460 /* open all sockets */
461 for (i
= 0; i
< no_bl_hosts
; i
++)
462 if (bl
->init_connection(&bl_hosts
[i
])) return 1;
468 static int control(uint32_t request
, void *data
) {
470 case VOCTRL_QUERY_FORMAT
:
471 return query_format(*((uint32_t*)data
));