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>
31 #include <sys/ioctl.h>
36 #define closesocket close
38 #include <sys/socket.h>
39 #include <netinet/in.h>
44 #include "video_out.h"
45 #include "video_out_internal.h"
48 #include "fastmemcpy.h"
50 static const vo_info_t info
=
52 "Blinkenlights driver: http://www.blinkenlights.de",
54 "Rik Snel <snel@phys.uu.nl>",
58 const LIBVO_EXTERN (bl
)
60 /* General variables */
62 static unsigned char *image
= NULL
;
63 static unsigned char *tmp
= NULL
;
65 static char *bl_subdevice
= NULL
;
66 static int prevpts
= -1;
69 char *name
; /* filename */
71 int header_written
; /* if header was written already */
75 char *name
; /* hostname */
77 int fd
; /* file descriptor */
87 int bpc
; /* bits per component: bpc = 3, channels = 3 => bpp = 24*/
89 /* file output functions */
90 int (*init_file
)(bl_file_t
*file
);
91 void (*write_frame
)(bl_file_t
*file
, unsigned char *i
, int duration
);
92 void (*close_file
)(bl_file_t
*file
);
94 /* network output functions */
95 int (*init_connection
)(bl_host_t
*host
);
96 void (*send_frame
)(bl_host_t
*host
);
97 void (*close_connection
)(bl_host_t
*host
);
100 static bl_properties_t
*bl
= NULL
;
102 /* arbitrary limit because I am too lazy to do proper memory management */
103 #define BL_MAX_FILES 16
104 #define BL_MAX_HOSTS 16
105 static bl_file_t bl_files
[BL_MAX_FILES
];
106 static bl_host_t bl_hosts
[BL_MAX_HOSTS
];
107 static int no_bl_files
= 0;
108 static int no_bl_hosts
= 0;
116 unsigned char data
[0];
119 static bl_packet_t
*bl_packet
= NULL
;
122 /* bml output functions */
123 static int bml_init(bl_file_t
*f
) {
124 f
->fp
= fopen(f
->name
, "w");
126 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: error opening %s\n", f
->name
);
129 f
->header_written
= 0;
133 static void bml_write_frame(bl_file_t
*f
, unsigned char *i
, int duration
) {
135 if( ! f
->header_written
)
138 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
139 "<blm width=\"%d\" height=\"%d\" bits=\"%d\" channels=\"%d\">\n"
141 " <title>Movie autogenerated by MPlayer</title>\n"
142 " <url>http://www.mplayerhq.hu</url>\n"
143 " </header>\n", bl
->width
, bl
->height
, bl
->bpc
, bl
->channels
);
144 f
->header_written
= 1;
146 fprintf(f
->fp
, " <frame duration=\"%d\">\n", duration
);
147 for (j
= 0; j
< bl
->height
; j
++) {
148 fprintf(f
->fp
, " <row>");
149 for (k
= 0; k
< bl
->width
; k
++)
150 fprintf(f
->fp
, "%02x", *(i
+ j
* bl
->width
+ k
));
151 fprintf(f
->fp
, "</row>\n");
153 fprintf(f
->fp
, " </frame>\n");
156 static void bml_close(bl_file_t
*f
) {
157 fprintf(f
->fp
, "</blm>\n");
161 /* Blinkenlights UDP protocol */
162 static int udp_init(bl_host_t
*h
) {
163 struct sockaddr_in addr
;
164 struct hostent
*dest
;
166 dest
= gethostbyname(h
->name
);
168 mp_msg(MSGT_VO
, MSGL_ERR
,
169 "unable to resolve host %s\n", h
->name
);
174 addr
.sin_family
= AF_INET
;
175 addr
.sin_port
= htons(h
->port
);
177 memcpy(&addr
.sin_addr
.s_addr
, dest
->h_addr_list
[0], dest
->h_length
);
179 h
->fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
181 mp_msg(MSGT_VO
, MSGL_ERR
,
182 "couldn't create socket for %s\n", h
->name
);
185 if (connect(h
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
186 mp_msg(MSGT_VO
, MSGL_ERR
, "couldn't connect socket for %s\n",
194 static void udp_send(bl_host_t
*h
) {
195 if (send(h
->fd
, bl_packet
, bl_size
, 0) != bl_size
)
196 mp_msg(MSGT_VO
, MSGL_ERR
, "unable to send to %s\n", h
->name
);
199 static void udp_close(bl_host_t
*h
) {
205 static bl_properties_t bls
[NO_BLS
] = {
206 { "hdl", IMGFMT_YV12
, 1, 18, 8, 8,
207 &bml_init
, &bml_write_frame
, &bml_close
,
208 &udp_init
, &udp_send
, &udp_close
},
209 { "arcade", IMGFMT_YV12
, 1, 26, 20, 8,
210 &bml_init
, &bml_write_frame
, &bml_close
,
211 &udp_init
, &udp_send
, &udp_close
},
212 { "grayscale", IMGFMT_YV12
, 1, -1, -1, 8, /* use width and height of movie */
213 &bml_init
, &bml_write_frame
, &bml_close
,
214 &udp_init
, &udp_send
, &udp_close
} };
216 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
217 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
221 /* adapt size of Blinkenlights UDP stream to size of movie */
222 if (bl
->width
< 0 || bl
->height
< 0) {
223 if (bl
->width
< 0) { /* use width of movie */
225 bl_packet
->width
= htons(bl
->width
);
227 if (bl
->height
< 0) { /* use height of movie */
229 bl_packet
->height
= htons(bl
->height
);
231 /* check for maximum size of UDP packet */
232 if (12 + bl
->width
*bl
->height
*bl
->channels
> 65507) {
233 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: %dx%d-%d does not fit into an UDP packet\n",
234 bl
->width
, bl
->height
, bl
->channels
);
237 /* resize frame and tmp buffers */
238 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
239 ptr
= realloc(bl_packet
, 12 + bl
->width
*bl
->height
*3); /* space for header and image data */
241 bl_packet
= (bl_packet_t
*)ptr
;
243 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
246 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
247 ptr
= realloc(tmp
, bl
->width
*bl
->height
*3); /* space for image data only */
249 tmp
= (unsigned char*)ptr
;
251 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
257 if (format
!= IMGFMT_YV12
) {
258 mp_msg(MSGT_VO
, MSGL_ERR
, "vo_bl called with wrong format");
261 if (width
> bl
->width
) {
262 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: width of movie too large %d > %d\n", width
, bl
->width
);
265 if (height
> bl
->height
) {
266 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: height of movie too large %d > %d\n", height
, bl
->height
);
270 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: image should be initialized, internal error\n");
273 memset(image
, 0, bl
->width
*bl
->height
*3); /* blank the image */
274 mp_msg(MSGT_VO
, MSGL_V
, "vo_config bl called\n");
278 static void draw_osd(void) {
281 static void flip_page (void) {
284 if (prevpts
>= 0) for (i
= 0; i
< no_bl_files
; i
++)
285 bl
->write_frame(&bl_files
[i
], tmp
, (vo_pts
- prevpts
)/90);
286 fast_memcpy(tmp
, image
, bl
->width
*bl
->height
*bl
->channels
);
289 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->send_frame(&bl_hosts
[i
]);
296 static int draw_frame(uint8_t * src
[]) {
300 static int query_format(uint32_t format
) {
301 if (format
== bl
->img_format
)
302 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
306 static void uninit(void) {
308 mp_msg(MSGT_VO
, MSGL_V
, "bl: uninit called\n");
313 for (i
= 0; i
< no_bl_files
; i
++) bl
->close_file(&bl_files
[i
]);
314 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->close_connection(&bl_hosts
[i
]);
320 static void check_events(void) {
323 static int draw_slice(uint8_t *srcimg
[], int stride
[],
324 int wf
, int hf
, int xf
, int yf
) {
327 uint8_t *src
=srcimg
[0];
328 w
= wf
; h
= hf
; x
= xf
; y
= yf
;
329 dst
=image
; /* + zr->off_y + zr->image_width*(y/zr->vdec)+x;*/
331 for (i
= 0; i
< h
; i
++) {
332 fast_memcpy(dst
,src
,w
);
340 static int preinit(const char *arg
) {
344 if (!arg
|| strlen(arg
) == 0) {
345 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must be given, example: -vo bl:arcade:host=localhost:2323\n");
349 bl_subdevice
= malloc(strlen(arg
) + 1);
351 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
356 mp_msg(MSGT_VO
, MSGL_V
, "bl: preinit called with %s\n", arg
);
357 for (i
= 0; i
< NO_BLS
; i
++) {
358 if (!strncmp(p
, bls
[i
].name
, strlen(bls
[i
].name
)))
363 for (i
= 0; i
< NO_BLS
; i
++)
364 if (strlen( txt
) + 4 + strlen( bls
[i
].name
) + 1 < sizeof(txt
))
365 sprintf( txt
+ strlen( txt
), "%s%s",
366 txt
[0] == 0 ? "" : i
== NO_BLS
- 1 ? " or " : ", ", bls
[i
].name
);
367 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must start with %s\nbl: i.e. -vo bl:arcade:host=localhost:2323\n", txt
);
371 p
+= strlen(bls
[i
].name
);
374 bl_hosts
[0].name
= "localhost";
375 bl_hosts
[0].port
= 2323;
376 mp_msg(MSGT_VO
, MSGL_V
, "bl: no hosts/files specified, using localhost:2323\n");
378 } else if (*p
!= ':') {
379 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: syntax error in subdevice\n");
386 if (!strncmp(p
, "file=", 5)) {
387 if (no_bl_files
== BL_MAX_FILES
) {
388 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of files reached (%d)\n", BL_MAX_FILES
);
392 while (*q
!= ',' && *q
!= '\0') q
++;
393 if (*q
== '\0') end
= 1;
395 bl_files
[no_bl_files
].name
= p
;
396 mp_msg(MSGT_VO
, MSGL_V
, "blfile[%d]: %s\n",
399 } else if (!strncmp(p
, "host=", 5)) {
400 if (no_bl_hosts
== BL_MAX_HOSTS
) {
401 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of hosts reached (%d)\n", BL_MAX_HOSTS
);
405 while (*q
!= ',' && *q
!= '\0' && *q
!= ':') q
++;
408 bl_hosts
[no_bl_hosts
].name
= p
;
409 bl_hosts
[no_bl_hosts
].port
= atoi(q
);
410 while (*q
!= ',' && *q
!= '\0') q
++;
411 if (*q
== '\0') end
= 1;
413 /* use default port */
414 if (*q
== '\0') end
= 1;
416 bl_hosts
[no_bl_hosts
].name
= p
;
417 bl_hosts
[no_bl_hosts
].port
= 2323;
419 mp_msg(MSGT_VO
, MSGL_V
,
420 "blhost[%d]: %s:%d\n",
422 bl_hosts
[no_bl_hosts
].port
);
425 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
);
431 if (bl
->width
>= 0 && bl
->height
>= 0) { /* size already known */
432 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
433 bl_packet
= malloc(12 + bl
->width
*bl
->height
*3); /* space for header and image data */
434 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
435 tmp
= malloc(bl
->width
*bl
->height
*3); /* space for image data only */
437 else { /* size unknown yet */
439 bl_packet
= malloc(12 + 3); /* space for header and a pixel */
440 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
441 tmp
= malloc(3); /* space for a pixel only */
444 if (!bl_packet
|| !tmp
) {
445 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
448 bl_packet
->magic
= htonl(0x23542666);
449 bl_packet
->width
= htons(bl
->width
);
450 bl_packet
->height
= htons(bl
->height
);
451 bl_packet
->channels
= htons(bl
->channels
);
452 bl_packet
->maxval
= htons((1 << bl
->bpc
) - 1);
455 for (i
= 0; i
< no_bl_files
; i
++)
456 if (bl
->init_file(&bl_files
[i
])) return 1;
458 /* open all sockets */
459 for (i
= 0; i
< no_bl_hosts
; i
++)
460 if (bl
->init_connection(&bl_hosts
[i
])) return 1;
466 static int control(uint32_t request
, void *data
, ...) {
468 case VOCTRL_QUERY_FORMAT
:
469 return query_format(*((uint32_t*)data
));