sync with en/mplayer.1 rev. 30677
[mplayer/glamo.git] / libvo / vo_zr.c
blobaf9cc9b00a4e0e3dc5f3529abf4753cdfc67e9ae
1 /*
2 * playback on Zoran cards
3 * copyright (C) 2001, 2003 Rik Snel
5 * This file is part of MPlayer.
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 /* $Id$ */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <sys/mman.h>
34 #include <sys/ioctl.h>
35 #include <linux/types.h>
36 #include <linux/videodev.h>
37 #include "config.h"
38 #include "videodev_mjpeg.h"
39 #include "video_out.h"
40 #include "video_out_internal.h"
41 #include "mp_msg.h"
42 #include "m_option.h"
43 #include "fastmemcpy.h"
44 #include "jpeg_enc.h"
45 #include "vo_zr.h"
47 static const vo_info_t info =
49 "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
50 "zr",
51 "Rik Snel <rsnel@cube.dyndns.org>",
55 const LIBVO_EXTERN (zr)
57 #define ZR_MAX_DEVICES 4
58 /* General variables */
60 typedef struct {
61 int width;
62 int height;
63 int xoff;
64 int yoff;
65 int set;
66 } geo_t;
68 static int zr_count = 1;
69 static int zr_parsing = 0;
70 static int framenum;
72 typedef struct {
73 /* commandline args given for this device (and defaults) */
74 int vdec, hdec; /* requested decimation 1,2,4 */
75 int fd; /* force decimation */
76 int xdoff, ydoff; /* offset from upperleft of screen
77 * default is 'centered' */
78 int quality; /* jpeg quality 1=best, 20=bad */
79 geo_t g; /* view window (zrcrop) */
80 char *device; /* /dev/video1 */
81 int bw; /* if bw == 1, display in black&white */
82 int norm; /* PAL/NTSC */
84 /* buffers + pointers + info */
86 unsigned char *image;
87 int image_width, image_height, size;
88 int off_y, off_c, stride; /* for use by 'draw slice/frame' */
90 unsigned char *buf; /* the jpeg images will be placed here */
91 jpeg_enc_t *j;
92 unsigned char *y_data, *u_data, *v_data; /* used by the jpeg encoder */
93 int y_stride, u_stride, v_stride; /* these point somewhere in image */
95 /* information for (and about) the zoran card */
97 int vdes; /* file descriptor of card */
98 int frame, synco, queue; /* buffer management */
99 struct mjpeg_sync zs; /* state information */
100 struct mjpeg_params p;
101 struct mjpeg_requestbuffers zrq;
102 struct video_capability vc; /* max resolution and so on */
103 int fields, stretchy; /* must the *image be interlaced
104 or stretched to fit on the screen? */
105 } zr_info_t;
107 static zr_info_t zr_info[ZR_MAX_DEVICES] = {
108 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0,
109 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
110 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0,
111 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
112 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0,
113 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
114 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0,
115 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
120 #define MJPEG_NBUFFERS 2
121 #define MJPEG_SIZE 1024*256
124 static int zoran_getcap(zr_info_t *zr)
126 char* dev = NULL;
128 if (zr->device)
129 dev = zr->device;
130 else {
131 struct stat vstat;
132 const char *devs[] = {
133 "/dev/video",
134 "/dev/video0",
135 "/dev/v4l/video0",
136 "/dev/v4l0",
137 "/dev/v4l",
138 NULL
140 int i = 0;
144 if ((stat(devs[i], &vstat) == 0) && S_ISCHR(vstat.st_mode))
146 dev = devs[i];
147 mp_msg(MSGT_VO, MSGL_V, "zr: found video device %s\n", dev);
148 break;
150 } while (devs[++i] != NULL);
152 if (!dev)
154 mp_msg(MSGT_VO, MSGL_ERR, "zr: unable to find video device\n");
155 return 1;
159 zr->vdes = open(dev, O_RDWR);
161 if (zr->vdes < 0) {
162 mp_msg(MSGT_VO, MSGL_ERR, "zr: error opening %s: %s\n",
163 dev, strerror(errno));
164 return 1;
167 /* before we can ask for the maximum resolution, we must set
168 * the correct tv norm */
170 if (ioctl(zr->vdes, MJPIOC_G_PARAMS, &zr->p) < 0) {
171 mp_msg(MSGT_VO, MSGL_ERR, "zr: device at %s is probably not a DC10(+)/buz/lml33\n", dev);
172 return 1;
175 if (zr->p.norm != zr->norm && zr->norm != VIDEO_MODE_AUTO) {
176 /* attempt to set requested norm */
177 zr->p.norm = zr->norm;
178 if (ioctl(zr->vdes, MJPIOC_S_PARAMS, &zr->p) < 0) {
179 mp_msg(MSGT_VO, MSGL_ERR,
180 "zr: unable to change video norm, use another program to change it (XawTV)\n");
181 return 1;
183 ioctl(zr->vdes, MJPIOC_G_PARAMS, &zr->p);
184 if (zr->norm != zr->p.norm) {
185 mp_msg(MSGT_VO, MSGL_ERR,
186 "zr: unable to change video norm, use another program to change it (XawTV)\n");
187 return 1;
191 if (ioctl(zr->vdes, VIDIOCGCAP, &zr->vc) < 0) {
192 mp_msg(MSGT_VO, MSGL_ERR, "zr: error getting video capabilities from %s\n", dev);
193 return 1;
195 mp_msg(MSGT_VO, MSGL_V, "zr: MJPEG card reports maxwidth=%d, maxheight=%d\n", zr->vc.maxwidth, zr->vc.maxheight);
197 return 0;
200 static int init_zoran(zr_info_t *zr, int stretchx, int stretchy)
202 /* center the image, and stretch it as far as possible (try to keep
203 * aspect) and check if it fits */
204 if (zr->image_width > zr->vc.maxwidth) {
205 mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too wide, max width currently %d\n", zr->vc.maxwidth);
206 return 1;
209 if (zr->image_height > zr->vc.maxheight) {
210 mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too high, max height currently %d\n", zr->vc.maxheight);
211 return 1;
214 zr->p.decimation = 0;
215 zr->p.HorDcm = stretchx;
216 zr->p.VerDcm = stretchy;
217 zr->p.TmpDcm = 1;
218 zr->p.field_per_buff = zr->fields;
219 if (zr->xdoff == -1) {
220 zr->p.img_x = (zr->vc.maxwidth -
221 zr->p.HorDcm*(int)zr->image_width/zr->hdec)/2;
222 } else {
223 zr->p.img_x = zr->xdoff;
225 if (zr->ydoff == -1) {
226 zr->p.img_y = (zr->vc.maxheight - zr->p.VerDcm*
227 (3-zr->fields)*(int)zr->image_height)/4;
228 } else {
229 zr->p.img_y = zr->ydoff;
231 zr->p.img_width = zr->p.HorDcm*zr->image_width/zr->hdec;
232 zr->p.img_height = zr->p.VerDcm*zr->image_height/zr->fields;
233 mp_msg(MSGT_VO, MSGL_V, "zr: geometry (after 'scaling'): %dx%d+%d+%d fields=%d, w=%d, h=%d\n", zr->p.img_width, (3-zr->fields)*zr->p.img_height, zr->p.img_x, zr->p.img_y, zr->fields, zr->image_width/zr->hdec, zr->image_height);
235 if (ioctl(zr->vdes, MJPIOC_S_PARAMS, &zr->p) < 0) {
236 mp_msg(MSGT_VO, MSGL_ERR, "zr: error setting display parameters\n");
237 return 1;
240 zr->zrq.count = MJPEG_NBUFFERS;
241 zr->zrq.size = MJPEG_SIZE;
243 if (ioctl(zr->vdes, MJPIOC_REQBUFS, &zr->zrq)) {
244 mp_msg(MSGT_VO, MSGL_ERR, "zr: error requesting %ld buffers of size %ld\n", zr->zrq.count, zr->zrq.size);
245 return 1;
248 /* the buffer count allocated may be different to the request */
249 zr->buf = (unsigned char*)mmap(0, zr->zrq.count*zr->zrq.size,
250 PROT_READ|PROT_WRITE, MAP_SHARED, zr->vdes, 0);
252 if (zr->buf == MAP_FAILED) {
253 mp_msg(MSGT_VO, MSGL_ERR, "zr: error requesting %ld buffers of size %ld\n", zr->zrq.count, zr->zrq.size);
254 return 1;
257 mp_msg(MSGT_VO, MSGL_V, "zr: got %ld buffers of size %ld (wanted %d buffers of size %d)\n", zr->zrq.count, zr->zrq.size, MJPEG_NBUFFERS, MJPEG_SIZE);
258 if (zr->zrq.count < MJPEG_NBUFFERS) {
259 mp_msg(MSGT_VO, MSGL_V, "zr: got not enough buffers\n");
260 return 1;
263 zr->queue = 0;
264 zr->synco = 0;
266 return 0;
269 static void uninit_zoran(zr_info_t *zr)
271 if (zr->image) {
272 free(zr->image);
273 zr->image=NULL;
275 while (zr->queue > zr->synco + 1) {
276 if (ioctl(zr->vdes, MJPIOC_SYNC, &zr->zs) < 0)
277 mp_msg(MSGT_VO, MSGL_ERR, "zr: error waiting for buffers to become free\n");
278 zr->synco++;
280 /* stop streaming */
281 zr->frame = -1;
282 if (ioctl(zr->vdes, MJPIOC_QBUF_PLAY, &zr->frame) < 0)
283 mp_msg(MSGT_VO, MSGL_ERR, "zr: error stopping playback of last frame\n");
284 if (munmap(zr->buf,zr->zrq.count*zr->zrq.size))
285 mp_msg(MSGT_VO, MSGL_ERR, "zr: error unmapping buffer\n");
286 close(zr->vdes);
289 static int zr_geometry_sane(geo_t *g, unsigned int width, unsigned int height)
291 if (g->set) {
292 if (g->width%2 != 0 || g->height%2 != 0 ||
293 g->xoff%2 != 0 || g->yoff%2 != 0) {
294 mp_msg(MSGT_VO, MSGL_ERR, "zr: arguments in -zrcrop must be multiples of 2\n");
295 return 1;
297 if (g->width <= 0 || g->height <= 0 ||
298 g->xoff < 0 || g->yoff < 0) {
299 mp_msg(MSGT_VO, MSGL_ERR, "zr: width and height must be positive and offset nonnegative\n");
300 return 1;
302 if (g->width + g->xoff > width) {
303 mp_msg(MSGT_VO, MSGL_ERR, "zr: width+xoffset (%d+%d>%d) is too big\n", g->width, g->xoff, width);
304 return 1;
306 if (g->height + g->yoff > height) {
307 mp_msg(MSGT_VO, MSGL_ERR, "zr: height+yoffset (%d+%d>%d) is too big\n", g->height, g->yoff, height);
308 return 1;
310 } else {
311 g->width = width;
312 g->height = height;
313 g->xoff = 0;
314 g->yoff = 0;
315 g->set = 1;
317 return 0;
321 static int config(uint32_t width, uint32_t height, uint32_t d_width,
322 uint32_t d_height, uint32_t flags, char *title, uint32_t format)
324 int i, tmp, stretchx, stretchy;
325 framenum = 0;
326 if (format != IMGFMT_YV12 && format != IMGFMT_YUY2) {
327 printf("vo_zr called with wrong format");
328 return 1;
330 for (i = 0; i < zr_count; i++) {
331 zr_info_t *zr = &zr_info[i];
332 geo_t *g = &zr->g;
334 zr->stride = 2*width;
335 if (zr_geometry_sane(g, width, height)) return 1;
337 /* we must know the maximum resolution of the device
338 * it differs for DC10+ and buz for example */
339 zoran_getcap(zr); /*must be called before init_zoran */
340 /* make the scaling decision
341 * we are capable of stretching the image in the horizontal
342 * direction by factors 1, 2 and 4
343 * we can stretch the image in the vertical direction by a
344 * factor of 1 and 2 AND we must decide about interlacing */
345 if (g->width > zr->vc.maxwidth/2 ||
346 g->height > zr->vc.maxheight/2) {
347 stretchx = 1;
348 stretchy = 1;
349 zr->fields = 2;
350 if (zr->vdec == 2) {
351 zr->fields = 1;
352 } else if (zr->vdec == 4) {
353 zr->fields = 1;
354 stretchy = 2;
356 stretchx = zr->hdec;
357 } else if (g->width > zr->vc.maxwidth/4 ||
358 g->height > zr->vc.maxheight/4) {
359 stretchx = 2;
360 stretchy = 1;
361 zr->fields = 1;
362 if (zr->vdec == 2) {
363 stretchy = 2;
364 } else if (zr->vdec == 4) {
365 if (!zr->fd) {
366 mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 2 (use -zrfd to keep vdec=4)\n");
367 zr->vdec = 2;
369 stretchy = 2;
371 if (zr->hdec == 2) {
372 stretchx = 4;
373 } else if (zr->hdec == 4){
374 if (!zr->fd) {
375 mp_msg(MSGT_VO, MSGL_WARN, "zr: horizontal decimation too high, changing to 2 (use -zrfd to keep hdec=4)\n");
376 zr->hdec = 2;
378 stretchx = 4;
380 } else {
381 /* output image is maximally stretched */
382 stretchx = 4;
383 stretchy = 2;
384 zr->fields = 1;
385 if (zr->vdec != 1 && !zr->fd) {
386 mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep vdec=%d)\n", zr->vdec);
387 zr->vdec = 1;
389 if (zr->hdec != 1 && !zr->fd) {
390 mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep hdec=%d)\n", zr->hdec);
391 zr->hdec = 1;
394 /* It can be that the original frame was too big for display,
395 * or that the width of the decimated image (for example) after
396 * padding up to a multiple of 16 has become too big. (orig
397 * width 720 (exactly right for the Buz) after decimation 360,
398 * after padding up to a multiple of 16 368, display 736 -> too
399 * large). In these situations we auto(re)crop. */
400 tmp = 16*((g->width - 1)/(zr->hdec*16) + 1);
401 if (stretchx*tmp > zr->vc.maxwidth) {
402 g->xoff += 2*((g->width - zr->hdec*(tmp-16))/4);
403 /* g->off must be a multiple of 2 */
404 g->width = zr->hdec*(tmp - 16);
405 g->set = 0; /* we abuse this field to
406 report that g has changed*/
408 tmp = 8*zr->fields*((g->height - 1)/(zr->vdec*zr->fields*8)+1);
409 if (stretchy*tmp > zr->vc.maxheight) {
410 g->yoff += 2*((g->height - zr->vdec*
411 (tmp - 8*zr->fields))/4);
412 g->height = zr->vdec*(tmp - 8*zr->fields);
413 g->set = 0;
415 if (!g->set)
416 mp_msg(MSGT_VO, MSGL_V, "zr: auto(re)cropping %dx%d+%d+%d to make the image fit on the screen\n", g->width, g->height, g->xoff, g->yoff);
418 /* the height must be a multiple of fields*8 and the width
419 * must be a multiple of 16 */
420 /* add some black borders to make it so, and center the image*/
421 zr->image_height = zr->fields*8*((g->height/zr->vdec - 1)/
422 (zr->fields*8) + 1);
423 zr->image_width = (zr->hdec*16)*((g->width - 1)/(zr->hdec*16) + 1);
424 zr->off_y = (zr->image_height - g->height/zr->vdec)/2;
425 if (zr->off_y%2 != 0) zr->off_y++;
426 zr->off_y *= zr->image_width;
427 zr->off_c = zr->off_y/4;
428 zr->off_y += (zr->image_width - g->width)/2;
429 if (zr->off_y%2 != 0) zr->off_y--;
430 zr->off_c += (zr->image_width - g->width)/4;
431 zr->size = zr->image_width*zr->image_height;
432 mp_msg(MSGT_VO, MSGL_V, "zr: input: %dx%d, cropped: %dx%d, output: %dx%d, off_y=%d, off_c=%d\n", width, height, g->width, g->height, zr->image_width, zr->image_height, zr->off_y, zr->off_c);
434 zr->image = malloc(2*zr->size); /* this buffer allows for YUV422 data,
435 * so it is a bit too big for YUV420 */
436 if (!zr->image) {
437 mp_msg(MSGT_VO, MSGL_ERR, "zr: Memory exhausted\n");
438 return 1;
440 /* and make sure that the borders are _really_ black */
441 switch (format) {
442 case IMGFMT_YV12:
443 memset(zr->image, 0, zr->size);
444 memset(zr->image + zr->size, 0x80, zr->size/4);
445 memset(zr->image + 3*zr->size/2, 0x80, zr->size/4);
446 zr->y_data = zr->image;
447 zr->u_data = zr->image + zr->size;
448 zr->v_data = zr->image + 3*zr->size/2;
450 zr->y_stride = zr->image_width;
451 zr->u_stride = zr->image_width/2;
452 zr->v_stride = zr->image_width/2;
454 zr->j = jpeg_enc_init(zr->image_width/zr->hdec,
455 zr->image_height/zr->fields,
456 zr->hdec, zr->y_stride*zr->fields,
457 zr->hdec, zr->u_stride*zr->fields,
458 zr->hdec, zr->v_stride*zr->fields,
459 1, zr->quality, zr->bw);
460 break;
461 case IMGFMT_YUY2:
462 for (tmp = 0; tmp < 2*zr->size; tmp+=4) {
463 zr->image[tmp] = 0;
464 zr->image[tmp+1] = 0x80;
465 zr->image[tmp+2] = 0;
466 zr->image[tmp+3] = 0x80;
469 zr->y_data = zr->image;
470 zr->u_data = zr->image + 1;
471 zr->v_data = zr->image + 3;
473 zr->y_stride = 2*zr->image_width;
474 zr->u_stride = 2*zr->image_width;
475 zr->v_stride = 2*zr->image_width;
477 zr->j = jpeg_enc_init(zr->image_width/zr->hdec,
478 zr->image_height/zr->fields,
479 zr->hdec*2,
480 zr->y_stride*zr->fields,
481 zr->hdec*4,
482 zr->u_stride*zr->fields,
483 zr->hdec*4,
484 zr->v_stride*zr->fields,
485 0, zr->quality, zr->bw);
486 break;
487 default:
488 mp_msg(MSGT_VO, MSGL_FATAL, "zr: internal inconsistency in vo_zr\n");
492 if (zr->j == NULL) {
493 mp_msg(MSGT_VO, MSGL_ERR, "zr: error initializing the jpeg encoder\n");
494 return 1;
497 if (init_zoran(zr, stretchx, stretchy)) {
498 return 1;
502 return 0;
505 static void draw_osd(void) {
508 static void flip_page (void) {
509 int i, j, k;
510 //FILE *fp;
511 //char filename[100];
512 /* do we have a free buffer? */
513 for (j = 0; j < zr_count; j++) {
514 zr_info_t *zr = &zr_info[j];
515 /* using MJPEG_NBUFFERS here, using the real number of
516 * buffers may give sync issues (real number of buffers
517 * is always sufficient) */
518 if (zr->queue-zr->synco < MJPEG_NBUFFERS) {
519 zr->frame = zr->queue;
520 } else {
521 if (ioctl(zr->vdes, MJPIOC_SYNC, &zr->zs) < 0)
522 mp_msg(MSGT_VO, MSGL_ERR, "zr: error waiting for buffers to become free\n");
523 zr->frame = zr->zs.frame;
524 zr->synco++;
526 k=0;
527 for (i = 0; i < zr->fields; i++)
528 k+=jpeg_enc_frame(zr->j, zr->y_data + i*zr->y_stride,
529 zr->u_data + i*zr->u_stride,
530 zr->v_data + i*zr->v_stride,
531 zr->buf + zr->frame*zr->zrq.size+k);
532 if (k > zr->zrq.size) mp_msg(MSGT_VO, MSGL_WARN, "zr: jpeg image too large for maximum buffer size. Lower the jpeg encoding\nquality or the resolution of the movie.\n");
534 /* Warning: Only the first jpeg image contains huffman- and
535 * quantisation tables, so don't expect files other than
536 * test0001.jpg to be readable */
537 /*sprintf(filename, "test%04d.jpg", framenum);
538 fp = fopen(filename, "w");
539 if (!fp) exit(1);
540 fwrite(buf+frame*zrq.size, 1, k, fp);
541 fclose(fp);*/
542 /*fp = fopen("test1.jpg", "r");
543 fread(buf+frame*zrq.size, 1, 2126, fp);
544 fclose(fp);*/
546 for (j = 0; j < zr_count; j++) {
547 zr_info_t *zr = &zr_info[j];
548 if (ioctl(zr->vdes, MJPIOC_QBUF_PLAY, &zr->frame) < 0)
549 mp_msg(MSGT_VO, MSGL_ERR, "zr: error queueing buffer for playback\n");
550 zr->queue++;
553 framenum++;
554 return;
557 static int draw_frame(uint8_t * src[]) {
558 int i, j;
559 char *source, *dest;
560 //printf("draw frame called\n");
561 for (j = 0; j < zr_count; j++) {
562 zr_info_t *zr = &zr_info[j];
563 geo_t *g = &zr->g;
564 source = src[0] + 2*g->yoff*zr->vdec*zr->stride + 2*g->xoff;
565 dest = zr->image + 2*zr->off_y;
566 for (i = 0; i < g->height/zr->vdec; i++) {
567 fast_memcpy(dest, source, zr->image_width*2);
568 dest += 2*zr->image_width;
569 source += zr->vdec*zr->stride;
572 return 0;
575 static int query_format(uint32_t format) {
576 if(format==IMGFMT_YV12 || format==IMGFMT_YUY2)
577 return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW;
578 return 0;
581 static void uninit(void) {
582 int j;
583 mp_msg(MSGT_VO, MSGL_V, "zr: uninit called\n");
584 for (j = 0; j < zr_count; j++) {
585 jpeg_enc_uninit(zr_info[j].j);
586 uninit_zoran(&zr_info[j]);
590 static void check_events(void) {
594 static int draw_slice(uint8_t *srcimg[], int stride[],
595 int wf, int hf, int xf, int yf) {
596 int i, j, w, h, x, y;
597 /* Apply 'geometry', crop unwanted parts */
598 uint8_t *dst;
599 //printf("before: w=%d, h=%d, x=%d, y=%d, src0=%p, src1=%p, src2=%p\n", w, h, x, y, srcimg[0], srcimg[1], srcimg[2]);
600 for (j = 0; j < zr_count; j++) {
601 uint8_t *src=srcimg[0];
602 uint8_t *src1=srcimg[1];
603 uint8_t *src2=srcimg[2];
604 zr_info_t *zr = &zr_info[j];
605 geo_t *g = &zr->g;
606 w = wf; h = hf; x = xf; y = yf;
607 if (x < g->xoff) {
608 src += g->xoff - x;
609 src1 += (g->xoff - x)/2;
610 src2 += (g->xoff - x)/2;
611 w -= g->xoff - x;
612 if (w < 0) break; //return 0;
613 x = 0 /*g.xoff*/;
614 } else {
615 x -= g->xoff;
617 if (x + w > g->width) {
618 w = g->width - x;
619 if (w < 0) break; //return 0;
621 if (y < g->yoff) {
622 src += (g->yoff - y)*stride[0];
623 src1 += ((g->yoff - y)/2)*stride[1];
624 src2 += ((g->yoff - y)/2)*stride[2];
625 h -= g->yoff - y;
626 if (h < 0) break; //return 0;
627 y = 0;
628 } else {
629 y -= g->yoff;
631 if (y + h > g->height) {
632 h = g->height - y;
633 if (h < 0) break; //return 0;
635 //printf("after: w=%d, h=%d, x=%d, y=%d, src0=%p, src1=%p, src2=%p\n", w, h, x, y, srcimg[0], srcimg[1], srcimg[2]);
636 dst=zr->image + zr->off_y + zr->image_width*(y/zr->vdec)+x;
637 // copy Y:
638 for (i = 0; i < h; i++) {
639 if ((i + x)%zr->vdec == 0) {
640 fast_memcpy(dst,src,w);
641 dst+=zr->image_width;
643 src+=stride[0];
646 if (!zr->bw) {
647 // copy U+V:
648 uint8_t *dst1=zr->image + zr->size + zr->off_c+ (y/(zr->vdec*2))*zr->image_width/2+(x/2);
649 uint8_t *dst2=zr->image + 3*zr->size/2 + zr->off_c +
650 (y/(zr->vdec*2))*
651 zr->image_width/2+(x/2);
652 for (i = 0; i< h/2; i++) {
653 if ((i+x/2)%zr->vdec == 0) {
654 fast_memcpy(dst1,src1,w/2);
655 fast_memcpy(dst2,src2,w/2);
656 dst1+=zr->image_width/2;
657 dst2+=zr->image_width/2;
659 src1+=stride[1];
660 src2+=stride[2];
664 return 0;
668 /* copied and adapted from vo_aa_parseoption */
670 vo_zr_parseoption(const m_option_t* conf, const char *opt, const char *param){
671 /* got an option starting with zr */
672 zr_info_t *zr = &zr_info[zr_parsing];
673 int i;
674 /* do WE need it ?, always */
675 if (!strcasecmp(opt, "zrdev")) {
676 if (param == NULL) return ERR_MISSING_PARAM;
677 //if ((i=getcolor(param))==-1) return ERR_OUT_OF_RANGE;
678 //aaopt_osdcolor=i;
679 free(zr->device);
680 zr->device = malloc(strlen(param)+1);
681 strcpy(zr->device, param);
682 mp_msg(MSGT_VO, MSGL_V, "zr: using device %s\n", zr->device);
683 return 1;
684 } else if (!strcasecmp(opt, "zrbw")) {
685 if (param != NULL) {
686 return ERR_OUT_OF_RANGE;
688 zr->bw = 1;
689 return 1;
690 } else if (!strcasecmp(opt, "zrfd")) {
691 if (param != NULL) {
692 return ERR_OUT_OF_RANGE;
694 zr->fd = 1;
695 return 1;
696 } else if (!strcasecmp(opt, "zrcrop")){
697 geo_t *g = &zr->g;
698 if (g->set == 1) {
699 zr_parsing++;
700 zr_count++;
701 zr = &zr_info[zr_parsing];
702 g = &zr->g;
703 if (zr_count > 4) {
704 mp_msg(MSGT_VO, MSGL_ERR, "zr: too many simultaneus display devices requested (max. is 4)\n");
705 return ERR_OUT_OF_RANGE;
708 if (param == NULL) return ERR_MISSING_PARAM;
709 if (sscanf(param, "%dx%d+%d+%d", &g->width, &g->height,
710 &g->xoff, &g->yoff) != 4) {
711 g->xoff = 0; g->yoff = 0;
712 if (sscanf(param, "%dx%d", &g->width, &g->height) != 2) {
713 mp_msg(MSGT_VO, MSGL_ERR, "zr: argument to -zrcrop must be of the form 352x288+16+0\n");
714 return ERR_OUT_OF_RANGE;
717 g->set = 1;
718 mp_msg(MSGT_VO, MSGL_V, "zr: cropping %s\n", param);
719 return 1;
720 }else if (!strcasecmp(opt, "zrhdec")) {
721 i = atoi(param);
722 if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE;
723 zr->hdec = i;
724 return 1;
725 }else if (!strcasecmp(opt, "zrvdec")) {
726 i = atoi(param);
727 if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE;
728 zr->vdec = i;
729 return 1;
730 }else if (!strcasecmp(opt, "zrxdoff")) {
731 i = atoi(param);
732 zr->xdoff = i;
733 return 1;
734 }else if (!strcasecmp(opt, "zrydoff")) {
735 i = atoi(param);
736 zr->ydoff = i;
737 return 1;
738 }else if (!strcasecmp(opt, "zrquality")) {
739 i = atoi(param);
740 if (i < 1 || i > 20) return ERR_OUT_OF_RANGE;
741 zr->quality = i;
742 return 1;
743 }else if (!strcasecmp(opt, "zrnorm")) {
744 if (param == NULL) return ERR_MISSING_PARAM;
745 if (!strcasecmp(param, "NTSC")) {
746 mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to NTSC\n");
747 zr->norm = VIDEO_MODE_NTSC;
748 return 1;
749 } else if (!strcasecmp(param, "PAL")) {
750 mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to PAL\n");
751 zr->norm = VIDEO_MODE_PAL;
752 return 1;
753 } else {
754 return ERR_OUT_OF_RANGE;
756 }else if (!strcasecmp(opt, "zrhelp")){
757 printf("Help for -vo zr: Zoran ZR360[56]7/ZR36060 based MJPEG capture/playback cards\n");
758 printf("\n");
759 printf("Here are the zr options:\n");
760 printf(
761 "\n"
762 " -zrcrop specify part of the input image that\n"
763 " you want to see as an x-style geometry string\n"
764 " example: -zrcrop 352x288+16+0\n"
765 " -zrvdec vertical decimation 1, 2 or 4\n"
766 " -zrhdec horizontal decimation 1, 2 or 4\n"
767 " -zrfd decimation is only done if the primitive\n"
768 " hardware upscaler can correct for the decimation,\n"
769 " this switch allows you to see the effects\n"
770 " of too much decimation\n"
771 " -zrbw display in black&white (speed increase)\n"
772 " -zrxdoff x offset from upper-left of TV screen (default is 'centered')\n"
773 " -zrydoff y offset from upper-left of TV screen (default is 'centered')\n"
774 " -zrquality jpeg compression quality [BEST] 1 - 20 [VERY BAD]\n"
775 " -zrdev playback device (example -zrdev /dev/video1)\n"
776 " -zrnorm specify norm PAL/NTSC (default: leave at current setting)\n"
777 "\n"
778 "Cinerama support: additional occurances of -zrcrop activate cinerama mode,\n"
779 "suppose you have a 704x272 movie, two DC10+ cards and two beamers (or tv's),\n"
780 "then you would issue the following command:\n\n"
781 "mplayer -vo zr -zrcrop 352x272+0+0 -zrdev /dev/video0 -zrcrop 352x272+352+0 \\\n"
782 " -zrdev /dev/video1 movie.avi\n\n"
783 "Options appearing after the second -zrcrop apply to the second card, it is\n"
784 "possible to dispay at a different jpeg quality or at different decimations.\n\n"
785 "The parameters -zrxdoff and -zrydoff can be used to align the two images.\n"
786 "The maximum number of zoran cards participating in cinerama is 4, so you can\n"
787 "build a 2x2 vidiwall. (untested for obvious reasons, the setup wit a buz and\n"
788 "a DC10+ (and no beamers) is tested, however)\n"
790 exit(0);
793 return ERR_NOT_AN_OPTION;
796 void vo_zr_revertoption(const m_option_t* opt,const char* param) {
798 zr_info_t *zr = &zr_info[1];
799 zr_count = 1;
800 zr_parsing = 0;
802 if (!strcasecmp(param, "zrdev")) {
803 if(zr->device)
804 free(zr->device);
805 zr->device=NULL;
806 } else if (!strcasecmp(param, "zrbw"))
807 zr->bw=0;
808 else if (!strcasecmp(param, "zrfd"))
809 zr->fd=0;
810 else if (!strcasecmp(param, "zrcrop"))
811 zr->g.set = zr->g.xoff = zr->g.yoff = 0;
812 else if (!strcasecmp(param, "zrhdec"))
813 zr->hdec = 1;
814 else if (!strcasecmp(param, "zrvdec"))
815 zr->vdec = 1;
816 else if (!strcasecmp(param, "zrxdoff"))
817 zr->xdoff = -1;
818 else if (!strcasecmp(param, "zrydoff"))
819 zr->ydoff = -1;
820 else if (!strcasecmp(param, "zrquality"))
821 zr->quality = 2;
822 else if (!strcasecmp(param, "zrnorm"))
823 zr->norm = VIDEO_MODE_AUTO;
827 static int preinit(const char *arg)
829 if(arg)
831 printf("vo_zr: Unknown subdevice: %s\n",arg);
832 return ENOSYS;
834 return 0;
837 static int control(uint32_t request, void *data, ...)
839 switch (request) {
840 case VOCTRL_QUERY_FORMAT:
841 return query_format(*((uint32_t*)data));
843 return VO_NOTIMPL;