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 memset(&addr
, 0, sizeof(addr
));
181 addr
.sin_family
= AF_INET
;
182 addr
.sin_port
= htons(h
->port
);
184 memcpy(&addr
.sin_addr
.s_addr
, dest
->h_addr_list
[0], dest
->h_length
);
186 h
->fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
188 mp_msg(MSGT_VO
, MSGL_ERR
,
189 "couldn't create socket for %s\n", h
->name
);
192 if (connect(h
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
193 mp_msg(MSGT_VO
, MSGL_ERR
, "couldn't connect socket for %s\n",
201 static void udp_send(bl_host_t
*h
) {
202 if (send(h
->fd
, bl_packet
, bl_size
, 0) != bl_size
)
203 mp_msg(MSGT_VO
, MSGL_ERR
, "unable to send to %s\n", h
->name
);
206 static void udp_close(bl_host_t
*h
) {
212 static bl_properties_t bls
[NO_BLS
] = {
213 { "hdl", IMGFMT_YV12
, 1, 18, 8, 8,
214 &bml_init
, &bml_write_frame
, &bml_close
,
215 &udp_init
, &udp_send
, &udp_close
},
216 { "arcade", IMGFMT_YV12
, 1, 26, 20, 8,
217 &bml_init
, &bml_write_frame
, &bml_close
,
218 &udp_init
, &udp_send
, &udp_close
},
219 { "grayscale", IMGFMT_YV12
, 1, -1, -1, 8, /* use width and height of movie */
220 &bml_init
, &bml_write_frame
, &bml_close
,
221 &udp_init
, &udp_send
, &udp_close
} };
223 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
224 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
228 /* adapt size of Blinkenlights UDP stream to size of movie */
229 if (bl
->width
< 0 || bl
->height
< 0) {
230 if (bl
->width
< 0) { /* use width of movie */
232 bl_packet
->width
= htons(bl
->width
);
234 if (bl
->height
< 0) { /* use height of movie */
236 bl_packet
->height
= htons(bl
->height
);
238 /* check for maximum size of UDP packet */
239 if (12 + bl
->width
*bl
->height
*bl
->channels
> 65507) {
240 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: %dx%d-%d does not fit into an UDP packet\n",
241 bl
->width
, bl
->height
, bl
->channels
);
244 /* resize frame and tmp buffers */
245 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
246 ptr
= realloc(bl_packet
, 12 + bl
->width
*bl
->height
*3); /* space for header and image data */
248 bl_packet
= (bl_packet_t
*)ptr
;
250 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
253 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
254 ptr
= realloc(tmp
, bl
->width
*bl
->height
*3); /* space for image data only */
256 tmp
= (unsigned char*)ptr
;
258 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
264 if (format
!= IMGFMT_YV12
) {
265 mp_msg(MSGT_VO
, MSGL_ERR
, "vo_bl called with wrong format");
268 if (width
> bl
->width
) {
269 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: width of movie too large %d > %d\n", width
, bl
->width
);
272 if (height
> bl
->height
) {
273 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: height of movie too large %d > %d\n", height
, bl
->height
);
277 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: image should be initialized, internal error\n");
280 memset(image
, 0, bl
->width
*bl
->height
*3); /* blank the image */
281 mp_msg(MSGT_VO
, MSGL_V
, "vo_config bl called\n");
285 static void draw_osd(void) {
288 static void flip_page (void) {
291 if (prevpts
>= 0) for (i
= 0; i
< no_bl_files
; i
++)
292 bl
->write_frame(&bl_files
[i
], tmp
, (vo_pts
- prevpts
)/90);
293 fast_memcpy(tmp
, image
, bl
->width
*bl
->height
*bl
->channels
);
296 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->send_frame(&bl_hosts
[i
]);
303 static int draw_frame(uint8_t * src
[]) {
307 static int query_format(uint32_t format
) {
308 if (format
== bl
->img_format
)
309 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
313 static void uninit(void) {
315 mp_msg(MSGT_VO
, MSGL_V
, "bl: uninit called\n");
320 for (i
= 0; i
< no_bl_files
; i
++) bl
->close_file(&bl_files
[i
]);
321 for (i
= 0; i
< no_bl_hosts
; i
++) bl
->close_connection(&bl_hosts
[i
]);
327 static void check_events(void) {
330 static int draw_slice(uint8_t *srcimg
[], int stride
[],
331 int wf
, int hf
, int xf
, int yf
) {
334 uint8_t *src
=srcimg
[0];
335 w
= wf
; h
= hf
; x
= xf
; y
= yf
;
336 dst
=image
; /* + zr->off_y + zr->image_width*(y/zr->vdec)+x;*/
338 for (i
= 0; i
< h
; i
++) {
339 fast_memcpy(dst
,src
,w
);
347 static int preinit(const char *arg
) {
351 if (!arg
|| strlen(arg
) == 0) {
352 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must be given, example: -vo bl:arcade:host=localhost:2323\n");
356 bl_subdevice
= malloc(strlen(arg
) + 1);
358 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
363 mp_msg(MSGT_VO
, MSGL_V
, "bl: preinit called with %s\n", arg
);
364 for (i
= 0; i
< NO_BLS
; i
++) {
365 if (!strncmp(p
, bls
[i
].name
, strlen(bls
[i
].name
)))
370 for (i
= 0; i
< NO_BLS
; i
++)
371 if (strlen( txt
) + 4 + strlen( bls
[i
].name
) + 1 < sizeof(txt
))
372 sprintf( txt
+ strlen( txt
), "%s%s",
373 txt
[0] == 0 ? "" : i
== NO_BLS
- 1 ? " or " : ", ", bls
[i
].name
);
374 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: subdevice must start with %s\nbl: i.e. -vo bl:arcade:host=localhost:2323\n", txt
);
378 p
+= strlen(bls
[i
].name
);
381 bl_hosts
[0].name
= "localhost";
382 bl_hosts
[0].port
= 2323;
383 mp_msg(MSGT_VO
, MSGL_V
, "bl: no hosts/files specified, using localhost:2323\n");
385 } else if (*p
!= ':') {
386 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: syntax error in subdevice\n");
393 if (!strncmp(p
, "file=", 5)) {
394 if (no_bl_files
== BL_MAX_FILES
) {
395 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of files reached (%d)\n", BL_MAX_FILES
);
399 while (*q
!= ',' && *q
!= '\0') q
++;
400 if (*q
== '\0') end
= 1;
402 bl_files
[no_bl_files
].name
= p
;
403 mp_msg(MSGT_VO
, MSGL_V
, "blfile[%d]: %s\n",
406 } else if (!strncmp(p
, "host=", 5)) {
407 if (no_bl_hosts
== BL_MAX_HOSTS
) {
408 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: maximum number of hosts reached (%d)\n", BL_MAX_HOSTS
);
412 while (*q
!= ',' && *q
!= '\0' && *q
!= ':') q
++;
415 bl_hosts
[no_bl_hosts
].name
= p
;
416 bl_hosts
[no_bl_hosts
].port
= atoi(q
);
417 while (*q
!= ',' && *q
!= '\0') q
++;
418 if (*q
== '\0') end
= 1;
420 /* use default port */
421 if (*q
== '\0') end
= 1;
423 bl_hosts
[no_bl_hosts
].name
= p
;
424 bl_hosts
[no_bl_hosts
].port
= 2323;
426 mp_msg(MSGT_VO
, MSGL_V
,
427 "blhost[%d]: %s:%d\n",
429 bl_hosts
[no_bl_hosts
].port
);
432 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
);
438 if (bl
->width
>= 0 && bl
->height
>= 0) { /* size already known */
439 bl_size
= 12 + bl
->width
*bl
->height
*bl
->channels
;
440 bl_packet
= malloc(12 + bl
->width
*bl
->height
*3); /* space for header and image data */
441 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
442 tmp
= malloc(bl
->width
*bl
->height
*3); /* space for image data only */
444 else { /* size unknown yet */
446 bl_packet
= malloc(12 + 3); /* space for header and a pixel */
447 image
= ((unsigned char*)bl_packet
+ 12); /* pointer to image data */
448 tmp
= malloc(3); /* space for a pixel only */
451 if (!bl_packet
|| !tmp
) {
452 mp_msg(MSGT_VO
, MSGL_ERR
, "bl: out of memory error\n");
455 bl_packet
->magic
= htonl(0x23542666);
456 bl_packet
->width
= htons(bl
->width
);
457 bl_packet
->height
= htons(bl
->height
);
458 bl_packet
->channels
= htons(bl
->channels
);
459 bl_packet
->maxval
= htons((1 << bl
->bpc
) - 1);
462 for (i
= 0; i
< no_bl_files
; i
++)
463 if (bl
->init_file(&bl_files
[i
])) return 1;
465 /* open all sockets */
466 for (i
= 0; i
< no_bl_hosts
; i
++)
467 if (bl
->init_connection(&bl_hosts
[i
])) return 1;
473 static int control(uint32_t request
, void *data
, ...) {
475 case VOCTRL_QUERY_FORMAT
:
476 return query_format(*((uint32_t*)data
));