2 * playback using the Blinkenlights UDP 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 * copyright (C) 2004 Stefan Schuermans <1stein@schuermans.info>
11 * other stuff: copyright (C) 2002 Rik Snel
13 * This file is part of MPlayer.
15 * MPlayer is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * MPlayer is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License along
26 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 #include <sys/types.h>
39 #ifdef HAVE_SYS_MMAN_H
42 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
50 #include "video_out.h"
51 #include "video_out_internal.h"
54 #include "fastmemcpy.h"
56 static const vo_info_t info
=
58 "Blinkenlights driver: http://www.blinkenlights.de",
60 "Rik Snel <snel@phys.uu.nl>",
64 const LIBVO_EXTERN (bl
)
66 /* General variables */
68 static unsigned char *image
= NULL
;
69 static unsigned char *tmp
= NULL
;
71 static char *bl_subdevice
= NULL
;
72 static int prevpts
= -1;
75 char *name
; /* filename */
77 int header_written
; /* if header was written already */
81 char *name
; /* hostname */
83 int fd
; /* file descriptor */
93 int bpc
; /* bits per component: bpc = 3, channels = 3 => bpp = 24*/
95 /* file output functions */
96 int (*init_file
)(bl_file_t
*file
);
97 void (*write_frame
)(bl_file_t
*file
, unsigned char *i
, int duration
);
98 void (*close_file
)(bl_file_t
*file
);
100 /* network output functions */
101 int (*init_connection
)(bl_host_t
*host
);
102 void (*send_frame
)(bl_host_t
*host
);
103 void (*close_connection
)(bl_host_t
*host
);
106 static bl_properties_t
*bl
= NULL
;
108 /* arbitrary limit because I am too lazy to do proper memory management */
109 #define BL_MAX_FILES 16
110 #define BL_MAX_HOSTS 16
111 static bl_file_t bl_files
[BL_MAX_FILES
];
112 static bl_host_t bl_hosts
[BL_MAX_HOSTS
];
113 static int no_bl_files
= 0;
114 static int no_bl_hosts
= 0;
122 unsigned char data
[0];
125 static bl_packet_t
*bl_packet
= NULL
;
128 /* bml output functions */
129 static int bml_init(bl_file_t
*f
) {
130 f
->fp
= fopen(f
->name
, "w");
132 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: error opening %s\n", f
->name
);
135 f
->header_written
= 0;
139 static void bml_write_frame(bl_file_t
*f
, unsigned char *i
, int duration
) {
141 if( ! f
->header_written
)
144 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
145 "<blm width=\"%d\" height=\"%d\" bits=\"%d\" channels=\"%d\">\n"
147 " <title>Movie autogenerated by MPlayer</title>\n"
148 " <url>http://www.mplayerhq.hu</url>\n"
149 " </header>\n", bl
->width
, bl
->height
, bl
->bpc
, bl
->channels
);
150 f
->header_written
= 1;
152 fprintf(f
->fp
, " <frame duration=\"%d\">\n", duration
);
153 for (j
= 0; j
< bl
->height
; j
++) {
154 fprintf(f
->fp
, " <row>");
155 for (k
= 0; k
< bl
->width
; k
++)
156 fprintf(f
->fp
, "%02x", *(i
+ j
* bl
->width
+ k
));
157 fprintf(f
->fp
, "</row>\n");
159 fprintf(f
->fp
, " </frame>\n");
162 static void bml_close(bl_file_t
*f
) {
163 fprintf(f
->fp
, "</blm>\n");
167 /* Blinkenlights UDP protocol */
168 static int udp_init(bl_host_t
*h
) {
169 struct sockaddr_in addr
;
170 struct hostent
*dest
;
172 dest
= gethostbyname(h
->name
);
174 mp_msg(MSGT_VO
, MSGL_ERR
,
175 "unable to resolve host %s\n", h
->name
);
180 addr
.sin_family
= AF_INET
;
181 addr
.sin_port
= htons(h
->port
);
183 memcpy(&addr
.sin_addr
.s_addr
, dest
->h_addr_list
[0], dest
->h_length
);
185 h
->fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
187 mp_msg(MSGT_VO
, MSGL_ERR
,
188 "couldn't create socket for %s\n", h
->name
);
191 if (connect(h
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
192 mp_msg(MSGT_VO
, MSGL_ERR
, "couldn't connect socket for %s\n",
200 static void udp_send(bl_host_t
*h
) {
201 if (send(h
->fd
, bl_packet
, bl_size
, 0) != bl_size
)
202 mp_msg(MSGT_VO
, MSGL_ERR
, "unable to send to %s\n", h
->name
);
205 static void udp_close(bl_host_t
*h
) {
211 static bl_properties_t bls
[NO_BLS
] = {
212 { "hdl", IMGFMT_YV12
, 1, 18, 8, 8,
213 &bml_init
, &bml_write_frame
, &bml_close
,
214 &udp_init
, &udp_send
, &udp_close
},
215 { "arcade", IMGFMT_YV12
, 1, 26, 20, 8,
216 &bml_init
, &bml_write_frame
, &bml_close
,
217 &udp_init
, &udp_send
, &udp_close
},
218 { "grayscale", IMGFMT_YV12
, 1, -1, -1, 8, /* use width and height of movie */
219 &bml_init
, &bml_write_frame
, &bml_close
,
220 &udp_init
, &udp_send
, &udp_close
} };
222 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
223 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
227 /* adapt size of Blinkenlights UDP stream to size of movie */
228 if (bl
->width
< 0 || bl
->height
< 0) {
229 if (bl
->width
< 0) { /* use width of movie */
231 bl_packet
->width
= htons(bl
->width
);
233 if (bl
->height
< 0) { /* use height of movie */
235 bl_packet
->height
= htons(bl
->height
);
237 /* check for maximum size of UDP packet */
238 if (12 + bl
->width
*bl
->height
*bl
->channels
> 65507) {
239 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: %dx%d-%d does not fit into an UDP packet\n",
240 bl
->width
, bl
->height
, bl
->channels
);
243 /* resize frame and tmp buffers */
244 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
245 ptr
= realloc(bl_packet
, 12 + bl
->width
*bl
->height
*3); /* space for header and image data */
247 bl_packet
= (bl_packet_t
*)ptr
;
249 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
252 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
253 ptr
= realloc(tmp
, bl
->width
*bl
->height
*3); /* space for image data only */
255 tmp
= (unsigned char*)ptr
;
257 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
263 if (format
!= IMGFMT_YV12
) {
264 mp_msg(MSGT_VO
, MSGL_ERR
, "vo_bl called with wrong format");
267 if (width
> bl
->width
) {
268 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: width of movie too large %d > %d\n", width
, bl
->width
);
271 if (height
> bl
->height
) {
272 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: height of movie too large %d > %d\n", height
, bl
->height
);
276 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: image should be initialized, internal error\n");
279 memset(image
, 0, bl
->width
*bl
->height
*3); /* blank the image */
280 mp_msg(MSGT_VO
, MSGL_V
, "vo_config bl called\n");
284 static void draw_osd(void) {
287 static void flip_page (void) {
290 if (prevpts
>= 0) for (i
= 0; i
< no_bl_files
; i
++)
291 bl
->write_frame(&bl_files
[i
], tmp
, (vo_pts
- prevpts
)/90);
292 fast_memcpy(tmp
, image
, bl
->width
*bl
->height
*bl
->channels
);
295 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->send_frame(&bl_hosts
[i
]);
302 static int draw_frame(uint8_t * src
[]) {
306 static int query_format(uint32_t format
) {
307 if (format
== bl
->img_format
)
308 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
312 static void uninit(void) {
314 mp_msg(MSGT_VO
, MSGL_V
, "bl: uninit called\n");
319 for (i
= 0; i
< no_bl_files
; i
++) bl
->close_file(&bl_files
[i
]);
320 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->close_connection(&bl_hosts
[i
]);
326 static void check_events(void) {
329 static int draw_slice(uint8_t *srcimg
[], int stride
[],
330 int wf
, int hf
, int xf
, int yf
) {
333 uint8_t *src
=srcimg
[0];
334 w
= wf
; h
= hf
; x
= xf
; y
= yf
;
335 dst
=image
; /* + zr->off_y + zr->image_width*(y/zr->vdec)+x;*/
337 for (i
= 0; i
< h
; i
++) {
338 fast_memcpy(dst
,src
,w
);
346 static int preinit(const char *arg
) {
350 if (!arg
|| strlen(arg
) == 0) {
351 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must be given, example: -vo bl:arcade:host=localhost:2323\n");
355 bl_subdevice
= malloc(strlen(arg
) + 1);
357 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
362 mp_msg(MSGT_VO
, MSGL_V
, "bl: preinit called with %s\n", arg
);
363 for (i
= 0; i
< NO_BLS
; i
++) {
364 if (!strncmp(p
, bls
[i
].name
, strlen(bls
[i
].name
)))
369 for (i
= 0; i
< NO_BLS
; i
++)
370 if (strlen( txt
) + 4 + strlen( bls
[i
].name
) + 1 < sizeof(txt
))
371 sprintf( txt
+ strlen( txt
), "%s%s",
372 txt
[0] == 0 ? "" : i
== NO_BLS
- 1 ? " or " : ", ", bls
[i
].name
);
373 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must start with %s\nbl: i.e. -vo bl:arcade:host=localhost:2323\n", txt
);
377 p
+= strlen(bls
[i
].name
);
380 bl_hosts
[0].name
= "localhost";
381 bl_hosts
[0].port
= 2323;
382 mp_msg(MSGT_VO
, MSGL_V
, "bl: no hosts/files specified, using localhost:2323\n");
384 } else if (*p
!= ':') {
385 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: syntax error in subdevice\n");
392 if (!strncmp(p
, "file=", 5)) {
393 if (no_bl_files
== BL_MAX_FILES
) {
394 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of files reached (%d)\n", BL_MAX_FILES
);
398 while (*q
!= ',' && *q
!= '\0') q
++;
399 if (*q
== '\0') end
= 1;
401 bl_files
[no_bl_files
].name
= p
;
402 mp_msg(MSGT_VO
, MSGL_V
, "blfile[%d]: %s\n",
405 } else if (!strncmp(p
, "host=", 5)) {
406 if (no_bl_hosts
== BL_MAX_HOSTS
) {
407 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of hosts reached (%d)\n", BL_MAX_HOSTS
);
411 while (*q
!= ',' && *q
!= '\0' && *q
!= ':') q
++;
414 bl_hosts
[no_bl_hosts
].name
= p
;
415 bl_hosts
[no_bl_hosts
].port
= atoi(q
);
416 while (*q
!= ',' && *q
!= '\0') q
++;
417 if (*q
== '\0') end
= 1;
419 /* use default port */
420 if (*q
== '\0') end
= 1;
422 bl_hosts
[no_bl_hosts
].name
= p
;
423 bl_hosts
[no_bl_hosts
].port
= 2323;
425 mp_msg(MSGT_VO
, MSGL_V
,
426 "blhost[%d]: %s:%d\n",
428 bl_hosts
[no_bl_hosts
].port
);
431 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
);
437 if (bl
->width
>= 0 && bl
->height
>= 0) { /* size already known */
438 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
439 bl_packet
= malloc(12 + bl
->width
*bl
->height
*3); /* space for header and image data */
440 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
441 tmp
= malloc(bl
->width
*bl
->height
*3); /* space for image data only */
443 else { /* size unknown yet */
445 bl_packet
= malloc(12 + 3); /* space for header and a pixel */
446 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
447 tmp
= malloc(3); /* space for a pixel only */
450 if (!bl_packet
|| !tmp
) {
451 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
454 bl_packet
->magic
= htonl(0x23542666);
455 bl_packet
->width
= htons(bl
->width
);
456 bl_packet
->height
= htons(bl
->height
);
457 bl_packet
->channels
= htons(bl
->channels
);
458 bl_packet
->maxval
= htons((1 << bl
->bpc
) - 1);
461 for (i
= 0; i
< no_bl_files
; i
++)
462 if (bl
->init_file(&bl_files
[i
])) return 1;
464 /* open all sockets */
465 for (i
= 0; i
< no_bl_hosts
; i
++)
466 if (bl
->init_connection(&bl_hosts
[i
])) return 1;
472 static int control(uint32_t request
, void *data
, ...) {
474 case VOCTRL_QUERY_FORMAT
:
475 return query_format(*((uint32_t*)data
));