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 #include <sys/socket.h>
39 #include <netinet/in.h>
41 #include "video_out.h"
42 #include "video_out_internal.h"
45 #include "fastmemcpy.h"
47 static const vo_info_t info
=
49 "Blinkenlights driver: http://www.blinkenlights.de",
51 "Rik Snel <snel@phys.uu.nl>",
55 const LIBVO_EXTERN (bl
)
57 /* General variables */
59 static unsigned char *image
= NULL
;
60 static unsigned char *tmp
= NULL
;
62 static char *bl_subdevice
= NULL
;
63 static int prevpts
= -1;
66 char *name
; /* filename */
68 int header_written
; /* if header was written already */
72 char *name
; /* hostname */
74 int fd
; /* file descriptor */
84 int bpc
; /* bits per component: bpc = 3, channels = 3 => bpp = 24*/
86 /* file output functions */
87 int (*init_file
)(bl_file_t
*file
);
88 void (*write_frame
)(bl_file_t
*file
, unsigned char *i
, int duration
);
89 void (*close_file
)(bl_file_t
*file
);
91 /* network output functions */
92 int (*init_connection
)(bl_host_t
*host
);
93 void (*send_frame
)(bl_host_t
*host
);
94 void (*close_connection
)(bl_host_t
*host
);
97 static bl_properties_t
*bl
= NULL
;
99 /* arbitrary limit because I am too lazy to do proper memory management */
100 #define BL_MAX_FILES 16
101 #define BL_MAX_HOSTS 16
102 static bl_file_t bl_files
[BL_MAX_FILES
];
103 static bl_host_t bl_hosts
[BL_MAX_HOSTS
];
104 static int no_bl_files
= 0;
105 static int no_bl_hosts
= 0;
113 unsigned char data
[0];
116 static bl_packet_t
*bl_packet
= NULL
;
119 /* bml output functions */
120 static int bml_init(bl_file_t
*f
) {
121 f
->fp
= fopen(f
->name
, "w");
123 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: error opening %s\n", f
->name
);
126 f
->header_written
= 0;
130 static void bml_write_frame(bl_file_t
*f
, unsigned char *i
, int duration
) {
132 if( ! f
->header_written
)
135 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
136 "<blm width=\"%d\" height=\"%d\" bits=\"%d\" channels=\"%d\">\n"
138 " <title>Movie autogenerated by MPlayer</title>\n"
139 " <url>http://www.mplayerhq.hu</url>\n"
140 " </header>\n", bl
->width
, bl
->height
, bl
->bpc
, bl
->channels
);
141 f
->header_written
= 1;
143 fprintf(f
->fp
, " <frame duration=\"%d\">\n", duration
);
144 for (j
= 0; j
< bl
->height
; j
++) {
145 fprintf(f
->fp
, " <row>");
146 for (k
= 0; k
< bl
->width
; k
++)
147 fprintf(f
->fp
, "%02x", *(i
+ j
* bl
->width
+ k
));
148 fprintf(f
->fp
, "</row>\n");
150 fprintf(f
->fp
, " </frame>\n");
153 static void bml_close(bl_file_t
*f
) {
154 fprintf(f
->fp
, "</blm>\n");
158 /* Blinkenlights UDP protocol */
159 static int udp_init(bl_host_t
*h
) {
160 struct sockaddr_in addr
;
161 struct hostent
*dest
;
163 dest
= gethostbyname(h
->name
);
165 mp_msg(MSGT_VO
, MSGL_ERR
,
166 "unable to resolve host %s\n", h
->name
);
171 addr
.sin_family
= AF_INET
;
172 addr
.sin_port
= htons(h
->port
);
174 memcpy(&addr
.sin_addr
.s_addr
, dest
->h_addr_list
[0], dest
->h_length
);
176 h
->fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
178 mp_msg(MSGT_VO
, MSGL_ERR
,
179 "couldn't create socket for %s\n", h
->name
);
182 if (connect(h
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
183 mp_msg(MSGT_VO
, MSGL_ERR
, "couldn't connect socket for %s\n",
191 static void udp_send(bl_host_t
*h
) {
192 if (send(h
->fd
, bl_packet
, bl_size
, 0) != bl_size
)
193 mp_msg(MSGT_VO
, MSGL_ERR
, "unable to send to %s\n", h
->name
);
196 static void udp_close(bl_host_t
*h
) {
202 static bl_properties_t bls
[NO_BLS
] = {
203 { "hdl", IMGFMT_YV12
, 1, 18, 8, 8,
204 &bml_init
, &bml_write_frame
, &bml_close
,
205 &udp_init
, &udp_send
, &udp_close
},
206 { "arcade", IMGFMT_YV12
, 1, 26, 20, 8,
207 &bml_init
, &bml_write_frame
, &bml_close
,
208 &udp_init
, &udp_send
, &udp_close
},
209 { "grayscale", IMGFMT_YV12
, 1, -1, -1, 8, /* use width and height of movie */
210 &bml_init
, &bml_write_frame
, &bml_close
,
211 &udp_init
, &udp_send
, &udp_close
} };
213 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
214 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
218 /* adapt size of Blinkenlights UDP stream to size of movie */
219 if (bl
->width
< 0 || bl
->height
< 0) {
220 if (bl
->width
< 0) { /* use width of movie */
222 bl_packet
->width
= htons(bl
->width
);
224 if (bl
->height
< 0) { /* use height of movie */
226 bl_packet
->height
= htons(bl
->height
);
228 /* check for maximum size of UDP packet */
229 if (12 + bl
->width
*bl
->height
*bl
->channels
> 65507) {
230 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: %dx%d-%d does not fit into an UDP packet\n",
231 bl
->width
, bl
->height
, bl
->channels
);
234 /* resize frame and tmp buffers */
235 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
236 ptr
= realloc(bl_packet
, 12 + bl
->width
*bl
->height
*3); /* space for header and image data */
238 bl_packet
= (bl_packet_t
*)ptr
;
240 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
243 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
244 ptr
= realloc(tmp
, bl
->width
*bl
->height
*3); /* space for image data only */
246 tmp
= (unsigned char*)ptr
;
248 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
254 if (format
!= IMGFMT_YV12
) {
255 mp_msg(MSGT_VO
, MSGL_ERR
, "vo_bl called with wrong format");
258 if (width
> bl
->width
) {
259 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: width of movie too large %d > %d\n", width
, bl
->width
);
262 if (height
> bl
->height
) {
263 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: height of movie too large %d > %d\n", height
, bl
->height
);
267 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: image should be initialized, internal error\n");
270 memset(image
, 0, bl
->width
*bl
->height
*3); /* blank the image */
271 mp_msg(MSGT_VO
, MSGL_V
, "vo_config bl called\n");
275 static void draw_osd(void) {
278 static void flip_page (void) {
281 if (prevpts
>= 0) for (i
= 0; i
< no_bl_files
; i
++)
282 bl
->write_frame(&bl_files
[i
], tmp
, (vo_pts
- prevpts
)/90);
283 fast_memcpy(tmp
, image
, bl
->width
*bl
->height
*bl
->channels
);
286 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->send_frame(&bl_hosts
[i
]);
293 static int draw_frame(uint8_t * src
[]) {
297 static int query_format(uint32_t format
) {
298 if (format
== bl
->img_format
)
299 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
303 static void uninit(void) {
305 mp_msg(MSGT_VO
, MSGL_V
, "bl: uninit called\n");
310 for (i
= 0; i
< no_bl_files
; i
++) bl
->close_file(&bl_files
[i
]);
311 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->close_connection(&bl_hosts
[i
]);
317 static void check_events(void) {
320 static int draw_slice(uint8_t *srcimg
[], int stride
[],
321 int wf
, int hf
, int xf
, int yf
) {
324 uint8_t *src
=srcimg
[0];
325 w
= wf
; h
= hf
; x
= xf
; y
= yf
;
326 dst
=image
; /* + zr->off_y + zr->image_width*(y/zr->vdec)+x;*/
328 for (i
= 0; i
< h
; i
++) {
329 fast_memcpy(dst
,src
,w
);
337 static int preinit(const char *arg
) {
341 if (!arg
|| strlen(arg
) == 0) {
342 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must be given, example: -vo bl:arcade:host=localhost:2323\n");
346 bl_subdevice
= malloc(strlen(arg
) + 1);
348 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
353 mp_msg(MSGT_VO
, MSGL_V
, "bl: preinit called with %s\n", arg
);
354 for (i
= 0; i
< NO_BLS
; i
++) {
355 if (!strncmp(p
, bls
[i
].name
, strlen(bls
[i
].name
)))
360 for (i
= 0; i
< NO_BLS
; i
++)
361 if (strlen( txt
) + 4 + strlen( bls
[i
].name
) + 1 < sizeof(txt
))
362 sprintf( txt
+ strlen( txt
), "%s%s",
363 txt
[0] == 0 ? "" : i
== NO_BLS
- 1 ? " or " : ", ", bls
[i
].name
);
364 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must start with %s\nbl: i.e. -vo bl:arcade:host=localhost:2323\n", txt
);
368 p
+= strlen(bls
[i
].name
);
371 bl_hosts
[0].name
= "localhost";
372 bl_hosts
[0].port
= 2323;
373 mp_msg(MSGT_VO
, MSGL_V
, "bl: no hosts/files specified, using localhost:2323\n");
375 } else if (*p
!= ':') {
376 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: syntax error in subdevice\n");
383 if (!strncmp(p
, "file=", 5)) {
384 if (no_bl_files
== BL_MAX_FILES
) {
385 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of files reached (%d)\n", BL_MAX_FILES
);
389 while (*q
!= ',' && *q
!= '\0') q
++;
390 if (*q
== '\0') end
= 1;
392 bl_files
[no_bl_files
].name
= p
;
393 mp_msg(MSGT_VO
, MSGL_V
, "blfile[%d]: %s\n",
396 } else if (!strncmp(p
, "host=", 5)) {
397 if (no_bl_hosts
== BL_MAX_HOSTS
) {
398 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of hosts reached (%d)\n", BL_MAX_HOSTS
);
402 while (*q
!= ',' && *q
!= '\0' && *q
!= ':') q
++;
405 bl_hosts
[no_bl_hosts
].name
= p
;
406 bl_hosts
[no_bl_hosts
].port
= atoi(q
);
407 while (*q
!= ',' && *q
!= '\0') q
++;
408 if (*q
== '\0') end
= 1;
410 /* use default port */
411 if (*q
== '\0') end
= 1;
413 bl_hosts
[no_bl_hosts
].name
= p
;
414 bl_hosts
[no_bl_hosts
].port
= 2323;
416 mp_msg(MSGT_VO
, MSGL_V
,
417 "blhost[%d]: %s:%d\n",
419 bl_hosts
[no_bl_hosts
].port
);
422 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
);
428 if (bl
->width
>= 0 && bl
->height
>= 0) { /* size already known */
429 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
430 bl_packet
= malloc(12 + bl
->width
*bl
->height
*3); /* space for header and image data */
431 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
432 tmp
= malloc(bl
->width
*bl
->height
*3); /* space for image data only */
434 else { /* size unknown yet */
436 bl_packet
= malloc(12 + 3); /* space for header and a pixel */
437 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
438 tmp
= malloc(3); /* space for a pixel only */
441 if (!bl_packet
|| !tmp
) {
442 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
445 bl_packet
->magic
= htonl(0x23542666);
446 bl_packet
->width
= htons(bl
->width
);
447 bl_packet
->height
= htons(bl
->height
);
448 bl_packet
->channels
= htons(bl
->channels
);
449 bl_packet
->maxval
= htons((1 << bl
->bpc
) - 1);
452 for (i
= 0; i
< no_bl_files
; i
++)
453 if (bl
->init_file(&bl_files
[i
])) return 1;
455 /* open all sockets */
456 for (i
= 0; i
< no_bl_hosts
; i
++)
457 if (bl
->init_connection(&bl_hosts
[i
])) return 1;
463 static int control(uint32_t request
, void *data
) {
465 case VOCTRL_QUERY_FORMAT
:
466 return query_format(*((uint32_t*)data
));