Document lmin and lmax lavcopts; mpeg vrc_buf_size values
[mplayer/greg.git] / libvo / vo_zr.c
blob90e133b47bc7639478c6da6f16048aa00ac054cd
1 /*
2 * vo_zr.c - playback on zoran cards
3 * Copyright (C) Rik Snel 2001,2002, License GNU GPL v2
4 */
6 /* $Id$ */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <errno.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <sys/time.h>
17 #include <sys/mman.h>
18 #include <sys/ioctl.h>
19 #include <linux/types.h>
20 #include <linux/videodev.h>
21 #include "videodev_mjpeg.h"
23 #include "config.h"
25 #include "video_out.h"
26 #include "video_out_internal.h"
27 #include "../mp_msg.h"
28 #include "../m_option.h"
29 #include "fastmemcpy.h"
31 #include "jpeg_enc.h"
33 static vo_info_t info =
35 "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
36 "zr",
37 "Rik Snel <snel@phys.uu.nl>",
41 LIBVO_EXTERN (zr)
43 #define ZR_MAX_DEVICES 4
44 /* General variables */
46 typedef struct {
47 int width;
48 int height;
49 int xoff;
50 int yoff;
51 int set;
52 } geo_t;
54 static int zr_count = 1;
55 static int zr_parsing = 0;
56 static int framenum;
58 typedef struct {
59 /* commandline args given for this device (and defaults) */
60 int vdec, hdec; /* requested decimation 1,2,4 */
61 int fd; /* force decimation */
62 int xdoff, ydoff; /* offset from upperleft of screen
63 * default is 'centered' */
64 int quality; /* jpeg quality 1=best, 20=bad */
65 geo_t g; /* view window (zrcrop) */
66 char *device; /* /dev/video1 */
67 int bw; /* if bw == 1, display in black&white */
68 int norm; /* PAL/NTSC */
69 int buffer_size; /* MJPEG buffer size */
71 /* buffers + pointers + info */
73 unsigned char *image;
74 int image_width, image_height, size;
75 int off_y, off_c, stride; /* for use by 'draw slice/frame' */
77 unsigned char *buf; /* the jpeg images will be placed here */
78 unsigned int buf_allocated; /* size of the block actually allocated */
79 jpeg_enc_t *j;
80 unsigned char *y_data, *u_data, *v_data; /* used by the jpeg encoder */
81 int y_stride, u_stride, v_stride; /* these point somewhere in image */
83 /* information for (and about) the zoran card */
85 int vdes; /* file descriptor of card */
86 int frame, synco, queue; /* buffer management */
87 struct mjpeg_sync zs; /* state information */
88 struct mjpeg_params p;
89 struct video_capability vc; /* max resolution and so on */
90 int fields, stretchy; /* must the *image be interlaced
91 or stretched to fit on the screen? */
92 } zr_info_t;
94 static zr_info_t zr_info[ZR_MAX_DEVICES] = {
95 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, 128, NULL, 0, 0, 0, 0, 0,
96 0, NULL, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
97 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, 128, NULL, 0, 0, 0, 0, 0,
98 0, NULL, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
99 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, 128, NULL, 0, 0, 0, 0, 0,
100 0, NULL, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
101 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, 128, NULL, 0, 0, 0, 0, 0,
102 0, NULL, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
107 #define MJPEG_NBUFFERS 2
108 /*#define MJPEG_SIZE 1024*128*/
111 int zoran_getcap(zr_info_t *zr) {
112 char* dev = NULL;
114 if (zr->device)
115 dev = zr->device;
116 else {
117 struct stat vstat;
118 const char *devs[] = {
119 "/dev/video",
120 "/dev/video0",
121 "/dev/v4l/video0",
122 "/dev/v4l0",
123 "/dev/v4l",
124 NULL
126 int i = 0;
130 if ((stat(devs[i], &vstat) == 0) && S_ISCHR(vstat.st_mode))
132 dev = devs[i];
133 mp_msg(MSGT_VO, MSGL_V, "zr: found video device %s\n", dev);
134 break;
136 } while (devs[++i] != NULL);
138 if (!dev)
140 mp_msg(MSGT_VO, MSGL_ERR, "zr: unable to find video device\n");
141 return 1;
145 zr->vdes = open(dev, O_RDWR);
147 if (zr->vdes < 0) {
148 mp_msg(MSGT_VO, MSGL_ERR, "zr: error opening %s: %s\n",
149 dev, strerror(errno));
150 return 1;
153 /* before we can ask for the maximum resolution, we must set
154 * the correct tv norm */
156 if (ioctl(zr->vdes, MJPIOC_G_PARAMS, &zr->p) < 0) {
157 mp_msg(MSGT_VO, MSGL_ERR, "zr: device at %s is probably not a DC10(+)/buz/lml33\n", dev);
158 return 1;
161 if (zr->p.norm != zr->norm && zr->norm != VIDEO_MODE_AUTO) {
162 /* attempt to set requested norm */
163 zr->p.norm = zr->norm;
164 if (ioctl(zr->vdes, MJPIOC_S_PARAMS, &zr->p) < 0) {
165 mp_msg(MSGT_VO, MSGL_ERR,
166 "zr: unable to change video norm, use another program to change it (XawTV)\n");
167 return 1;
169 ioctl(zr->vdes, MJPIOC_G_PARAMS, &zr->p);
170 if (zr->norm != zr->p.norm) {
171 mp_msg(MSGT_VO, MSGL_ERR,
172 "zr: unable to change video norm, use another program to change it (XawTV)\n");
173 return 1;
177 if (ioctl(zr->vdes, VIDIOCGCAP, &zr->vc) < 0) {
178 mp_msg(MSGT_VO, MSGL_ERR, "zr: error getting video capabilities from %s\n", dev);
179 return 1;
181 mp_msg(MSGT_VO, MSGL_V, "zr: MJPEG card reports maxwidth=%d, maxheight=%d\n", zr->vc.maxwidth, zr->vc.maxheight);
183 return 0;
186 int init_zoran(zr_info_t *zr, int stretchx, int stretchy) {
187 struct mjpeg_requestbuffers zrq;
188 /* center the image, and stretch it as far as possible (try to keep
189 * aspect) and check if it fits */
190 if (zr->image_width > zr->vc.maxwidth) {
191 mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too wide, max width currenty %d\n", zr->vc.maxwidth);
192 return 1;
195 if (zr->image_height > zr->vc.maxheight) {
196 mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too high, max height currenty %d\n", zr->vc.maxheight);
197 return 1;
200 zr->p.decimation = 0;
201 zr->p.HorDcm = stretchx;
202 zr->p.VerDcm = stretchy;
203 zr->p.TmpDcm = 1;
204 zr->p.field_per_buff = zr->fields;
205 if (zr->xdoff == -1) {
206 zr->p.img_x = (zr->vc.maxwidth -
207 zr->p.HorDcm*(int)zr->image_width/zr->hdec)/2;
208 } else {
209 zr->p.img_x = zr->xdoff;
211 if (zr->ydoff == -1) {
212 zr->p.img_y = (zr->vc.maxheight - zr->p.VerDcm*
213 (3-zr->fields)*(int)zr->image_height)/4;
214 } else {
215 zr->p.img_y = zr->ydoff;
217 zr->p.img_width = zr->p.HorDcm*zr->image_width/zr->hdec;
218 zr->p.img_height = zr->p.VerDcm*zr->image_height/zr->fields;
219 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);
221 if (ioctl(zr->vdes, MJPIOC_S_PARAMS, &zr->p) < 0) {
222 mp_msg(MSGT_VO, MSGL_ERR, "zr: error setting display parameters\n");
223 return 1;
226 zrq.count = MJPEG_NBUFFERS;
227 zrq.size = 1024*zr->buffer_size;
229 if (ioctl(zr->vdes, MJPIOC_REQBUFS, &zrq)) {
230 mp_msg(MSGT_VO, MSGL_ERR, "zr: error requesting %d buffers of size %d\n", zrq.count, zrq.size);
231 return 1;
234 /* the buffer count allocated may be different to the request */
235 zr->buf_allocated = zrq.count * zrq.size;
236 zr->buf = (unsigned char*)mmap(0, zrq.count*zrq.size,
237 PROT_READ|PROT_WRITE, MAP_SHARED, zr->vdes, 0);
239 if (zr->buf == MAP_FAILED) {
240 mp_msg(MSGT_VO, MSGL_ERR, "zr: error requesting %d buffers of size %d\n", zrq.count, zrq.size);
241 return 1;
244 zr->queue = 0;
245 zr->synco = 0;
247 return 0;
250 void uninit_zoran(zr_info_t *zr) {
251 if (zr->image) {
252 free(zr->image);
253 zr->image=NULL;
255 while (zr->queue > zr->synco + 1) {
256 if (ioctl(zr->vdes, MJPIOC_SYNC, &zr->zs) < 0)
257 mp_msg(MSGT_VO, MSGL_ERR, "zr: error waiting for buffers to become free\n");
258 zr->synco++;
260 /* stop streaming */
261 zr->frame = -1;
262 if (ioctl(zr->vdes, MJPIOC_QBUF_PLAY, &zr->frame) < 0)
263 mp_msg(MSGT_VO, MSGL_ERR, "zr: error stopping playback of last frame\n");
264 if (munmap(zr->buf,zr->buf_allocated))
265 mp_msg(MSGT_VO, MSGL_ERR, "zr: error unmapping buffer\n");
266 close(zr->vdes);
269 int zr_geometry_sane(geo_t *g, unsigned int width, unsigned int height) {
270 if (g->set) {
271 if (g->width%2 != 0 || g->height%2 != 0 ||
272 g->xoff%2 != 0 || g->yoff%2 != 0) {
273 mp_msg(MSGT_VO, MSGL_ERR, "zr: arguments in -zrcrop must be multiples of 2\n");
274 return 1;
276 if (g->width <= 0 || g->height <= 0 ||
277 g->xoff < 0 || g->yoff < 0) {
278 mp_msg(MSGT_VO, MSGL_ERR, "zr: width and height must be positive and offset nonnegative\n");
279 return 1;
281 if (g->width + g->xoff > width) {
282 mp_msg(MSGT_VO, MSGL_ERR, "zr: width+xoffset (%d+%d>%d) is too big\n", g->width, g->xoff, width);
283 return 1;
285 if (g->height + g->yoff > height) {
286 mp_msg(MSGT_VO, MSGL_ERR, "zr: height+yoffset (%d+%d>%d) is too big\n", g->height, g->yoff, height);
287 return 1;
289 } else {
290 g->width = width;
291 g->height = height;
292 g->xoff = 0;
293 g->yoff = 0;
294 g->set = 1;
296 return 0;
300 static uint32_t config(uint32_t width, uint32_t height, uint32_t d_width,
301 uint32_t d_height, uint32_t fullscreen, char *title, uint32_t format)
303 int i, tmp, stretchx, stretchy;
304 framenum = 0;
305 if (format != IMGFMT_YV12 && format != IMGFMT_YUY2) {
306 printf("vo_zr called with wrong format");
307 return 1;
309 for (i = 0; i < zr_count; i++) {
310 zr_info_t *zr = &zr_info[i];
311 geo_t *g = &zr->g;
313 zr->stride = 2*width;
314 if (zr_geometry_sane(g, width, height)) return 1;
316 /* we must know the maximum resolution of the device
317 * it differs for DC10+ and buz for example */
318 zoran_getcap(zr); /*must be called before init_zoran */
319 /* make the scaling decision
320 * we are capable of stretching the image in the horizontal
321 * direction by factors 1, 2 and 4
322 * we can stretch the image in the vertical direction by a
323 * factor of 1 and 2 AND we must decide about interlacing */
324 if (g->width > zr->vc.maxwidth/2 ||
325 g->height > zr->vc.maxheight/2) {
326 stretchx = 1;
327 stretchy = 1;
328 zr->fields = 2;
329 if (zr->vdec == 2) {
330 zr->fields = 1;
331 } else if (zr->vdec == 4) {
332 zr->fields = 1;
333 stretchy = 2;
335 stretchx = zr->hdec;
336 } else if (g->width > zr->vc.maxwidth/4 ||
337 g->height > zr->vc.maxheight/4) {
338 stretchx = 2;
339 stretchy = 1;
340 zr->fields = 1;
341 if (zr->vdec == 2) {
342 stretchy = 2;
343 } else if (zr->vdec == 4) {
344 if (!zr->fd) {
345 mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 2 (use -zrfd to keep vdec=4)\n");
346 zr->vdec = 2;
348 stretchy = 2;
350 if (zr->hdec == 2) {
351 stretchx = 4;
352 } else if (zr->hdec == 4){
353 if (!zr->fd) {
354 mp_msg(MSGT_VO, MSGL_WARN, "zr: horizontal decimation too high, changing to 2 (use -zrfd to keep hdec=4)\n");
355 zr->hdec = 2;
357 stretchx = 4;
359 } else {
360 /* output image is maximally stretched */
361 stretchx = 4;
362 stretchy = 2;
363 zr->fields = 1;
364 if (zr->vdec != 1 && !zr->fd) {
365 mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep vdec=%d)\n", zr->vdec);
366 zr->vdec = 1;
368 if (zr->hdec != 1 && !zr->fd) {
369 mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep hdec=%d)\n", zr->hdec);
370 zr->hdec = 1;
373 /* It can be that the original frame was too big for display,
374 * or that the width of the decimated image (for example) after
375 * padding up to a multiple of 16 has become too big. (orig
376 * width 720 (exactly right for the Buz) after decimation 360,
377 * after padding up to a multiple of 16 368, display 736 -> too
378 * large). In these situations we auto(re)crop. */
379 tmp = 16*((g->width - 1)/(zr->hdec*16) + 1);
380 if (stretchx*tmp > zr->vc.maxwidth) {
381 g->xoff += 2*((g->width - zr->hdec*(tmp-16))/4);
382 /* g->off must be a multiple of 2 */
383 g->width = zr->hdec*(tmp - 16);
384 g->set = 0; /* we abuse this field to
385 report that g has changed*/
387 tmp = 8*zr->fields*((g->height - 1)/(zr->vdec*zr->fields*8)+1);
388 if (stretchy*tmp > zr->vc.maxheight) {
389 g->yoff += 2*((g->height - zr->vdec*
390 (tmp - 8*zr->fields))/4);
391 g->height = zr->vdec*(tmp - 8*zr->fields);
392 g->set = 0;
394 if (!g->set)
395 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);
397 /* the height must be a multiple of fields*8 and the width
398 * must be a multiple of 16 */
399 /* add some black borders to make it so, and center the image*/
400 zr->image_height = zr->fields*8*((g->height/zr->vdec - 1)/
401 (zr->fields*8) + 1);
402 zr->image_width = (zr->hdec*16)*((g->width - 1)/(zr->hdec*16) + 1);
403 zr->off_y = (zr->image_height - g->height/zr->vdec)/2;
404 if (zr->off_y%2 != 0) zr->off_y++;
405 zr->off_y *= zr->image_width;
406 zr->off_c = zr->off_y/4;
407 zr->off_y += (zr->image_width - g->width)/2;
408 if (zr->off_y%2 != 0) zr->off_y--;
409 zr->off_c += (zr->image_width - g->width)/4;
410 zr->size = zr->image_width*zr->image_height;
411 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);
413 zr->image = malloc(2*zr->size); /* this buffer allows for YUV422 data,
414 * so it is a bit too big for YUV420 */
415 if (!zr->image) {
416 mp_msg(MSGT_VO, MSGL_ERR, "zr: Memory exhausted\n");
417 return 1;
419 /* and make sure that the borders are _really_ black */
420 switch (format) {
421 case IMGFMT_YV12:
422 memset(zr->image, 0, zr->size);
423 memset(zr->image + zr->size, 0x80, zr->size/4);
424 memset(zr->image + 3*zr->size/2, 0x80, zr->size/4);
425 zr->y_data = zr->image;
426 zr->u_data = zr->image + zr->size;
427 zr->v_data = zr->image + 3*zr->size/2;
429 zr->y_stride = zr->image_width;
430 zr->u_stride = zr->image_width/2;
431 zr->v_stride = zr->image_width/2;
433 zr->j = jpeg_enc_init(zr->image_width/zr->hdec,
434 zr->image_height/zr->fields,
435 zr->hdec, zr->y_stride*zr->fields,
436 zr->hdec, zr->u_stride*zr->fields,
437 zr->hdec, zr->v_stride*zr->fields,
438 1, zr->quality, zr->bw);
439 break;
440 case IMGFMT_YUY2:
441 for (tmp = 0; tmp < 2*zr->size; tmp+=4) {
442 zr->image[tmp] = 0;
443 zr->image[tmp+1] = 0x80;
444 zr->image[tmp+2] = 0;
445 zr->image[tmp+3] = 0x80;
448 zr->y_data = zr->image;
449 zr->u_data = zr->image + 1;
450 zr->v_data = zr->image + 3;
452 zr->y_stride = 2*zr->image_width;
453 zr->u_stride = 2*zr->image_width;
454 zr->v_stride = 2*zr->image_width;
456 zr->j = jpeg_enc_init(zr->image_width/zr->hdec,
457 zr->image_height/zr->fields,
458 zr->hdec*2,
459 zr->y_stride*zr->fields,
460 zr->hdec*4,
461 zr->u_stride*zr->fields,
462 zr->hdec*4,
463 zr->v_stride*zr->fields,
464 0, zr->quality, zr->bw);
465 break;
466 default:
467 mp_msg(MSGT_VO, MSGL_FATAL, "zr: internal inconsistency in vo_zr\n");
471 if (zr->j == NULL) {
472 mp_msg(MSGT_VO, MSGL_ERR, "zr: error initializing the jpeg encoder\n");
473 return 1;
476 if (init_zoran(zr, stretchx, stretchy)) {
477 return 1;
481 return 0;
484 static void draw_osd(void) {
487 static void flip_page (void) {
488 int i, j, k;
489 //FILE *fp;
490 //char filename[100];
491 /* do we have a free buffer? */
492 for (j = 0; j < zr_count; j++) {
493 zr_info_t *zr = &zr_info[j];
494 if (zr->queue-zr->synco < MJPEG_NBUFFERS) {
495 zr->frame = zr->queue;
496 } else {
497 if (ioctl(zr->vdes, MJPIOC_SYNC, &zr->zs) < 0)
498 mp_msg(MSGT_VO, MSGL_ERR, "zr: error waiting for buffers to become free\n");
499 zr->frame = zr->zs.frame;
500 zr->synco++;
502 k=0;
503 for (i = 0; i < zr->fields; i++)
504 k+=jpeg_enc_frame(zr->j, zr->y_data + i*zr->y_stride,
505 zr->u_data + i*zr->u_stride,
506 zr->v_data + i*zr->v_stride,
507 zr->buf+
508 1024*zr->frame*zr->buffer_size+k);
509 if (k > 1024*zr->buffer_size) mp_msg(MSGT_VO, MSGL_WARN, "zr: jpeg image too large or buffer size too small, try -zrbsize 256. If your\nmotherboard/card combo can't handle that: lower the jpeg encoding quality\nor the resolution of the movie. Image may become distorted, MPlayer may crash.\nDon't bugreport, it is a known problem. The standard buffer size of 128kB\nshould be sufficient and is a safe-for-almost-all choice.\n");
511 /* Warning: Only the first jpeg image contains huffman- and
512 * quantisation tables, so don't expect files other than
513 * test0001.jpg to be readable */
514 /*sprintf(filename, "test%04d.jpg", framenum);
515 fp = fopen(filename, "w");
516 if (!fp) exit(1);
517 fwrite(buf+frame*zrq.size, 1, k, fp);
518 fclose(fp);*/
519 /*fp = fopen("test1.jpg", "r");
520 fread(buf+frame*zrq.size, 1, 2126, fp);
521 fclose(fp);*/
523 for (j = 0; j < zr_count; j++) {
524 zr_info_t *zr = &zr_info[j];
525 if (ioctl(zr->vdes, MJPIOC_QBUF_PLAY, &zr->frame) < 0)
526 mp_msg(MSGT_VO, MSGL_ERR, "zr: error queueing buffer for playback\n");
527 zr->queue++;
530 framenum++;
531 return;
534 static uint32_t draw_frame(uint8_t * src[]) {
535 int i, j;
536 char *source, *dest;
537 //printf("draw frame called\n");
538 for (j = 0; j < zr_count; j++) {
539 zr_info_t *zr = &zr_info[j];
540 geo_t *g = &zr->g;
541 source = src[0] + 2*g->yoff*zr->vdec*zr->stride + 2*g->xoff;
542 dest = zr->image + 2*zr->off_y;
543 for (i = 0; i < g->height/zr->vdec; i++) {
544 memcpy(dest, source, zr->image_width*2);
545 dest += 2*zr->image_width;
546 source += zr->vdec*zr->stride;
549 return 0;
552 static uint32_t query_format(uint32_t format) {
553 if(format==IMGFMT_YV12 || format==IMGFMT_YUY2)
554 return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW;
555 return 0;
558 static void uninit(void) {
559 int j;
560 mp_msg(MSGT_VO, MSGL_V, "zr: uninit called\n");
561 for (j = 0; j < zr_count; j++) {
562 jpeg_enc_uninit(zr_info[j].j);
563 uninit_zoran(&zr_info[j]);
567 static void check_events(void) {
571 static uint32_t draw_slice(uint8_t *srcimg[], int stride[],
572 int wf, int hf, int xf, int yf) {
573 int i, j, w, h, x, y;
574 /* Apply 'geometry', crop unwanted parts */
575 uint8_t *dst;
576 //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]);
577 for (j = 0; j < zr_count; j++) {
578 uint8_t *src=srcimg[0];
579 uint8_t *src1=srcimg[1];
580 uint8_t *src2=srcimg[2];
581 zr_info_t *zr = &zr_info[j];
582 geo_t *g = &zr->g;
583 w = wf; h = hf; x = xf; y = yf;
584 if (x < g->xoff) {
585 src += g->xoff - x;
586 src1 += (g->xoff - x)/2;
587 src2 += (g->xoff - x)/2;
588 w -= g->xoff - x;
589 if (w < 0) break; //return 0;
590 x = 0 /*g.xoff*/;
591 } else {
592 x -= g->xoff;
594 if (x + w > g->width) {
595 w = g->width - x;
596 if (w < 0) break; //return 0;
598 if (y < g->yoff) {
599 src += (g->yoff - y)*stride[0];
600 src1 += ((g->yoff - y)/2)*stride[1];
601 src2 += ((g->yoff - y)/2)*stride[2];
602 h -= g->yoff - y;
603 if (h < 0) break; //return 0;
604 y = 0;
605 } else {
606 y -= g->yoff;
608 if (y + h > g->height) {
609 h = g->height - y;
610 if (h < 0) break; //return 0;
612 //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]);
613 dst=zr->image + zr->off_y + zr->image_width*(y/zr->vdec)+x;
614 // copy Y:
615 for (i = 0; i < h; i++) {
616 if ((i + x)%zr->vdec == 0) {
617 memcpy(dst,src,w);
618 dst+=zr->image_width;
620 src+=stride[0];
623 if (!zr->bw) {
624 // copy U+V:
625 uint8_t *dst1=zr->image + zr->size + zr->off_c+ (y/(zr->vdec*2))*zr->image_width/2+(x/2);
626 uint8_t *dst2=zr->image + 3*zr->size/2 + zr->off_c +
627 (y/(zr->vdec*2))*
628 zr->image_width/2+(x/2);
629 for (i = 0; i< h/2; i++) {
630 if ((i+x/2)%zr->vdec == 0) {
631 memcpy(dst1,src1,w/2);
632 memcpy(dst2,src2,w/2);
633 dst1+=zr->image_width/2;
634 dst2+=zr->image_width/2;
636 src1+=stride[1];
637 src2+=stride[2];
641 return 0;
645 /* copied and adapted from vo_aa_parseoption */
647 vo_zr_parseoption(m_option_t* conf, char *opt, char *param){
648 /* got an option starting with zr */
649 zr_info_t *zr = &zr_info[zr_parsing];
650 int i;
651 /* do WE need it ?, always */
652 if (!strcasecmp(opt, "zrdev")) {
653 if (param == NULL) return ERR_MISSING_PARAM;
654 //if ((i=getcolor(param))==-1) return ERR_OUT_OF_RANGE;
655 //aaopt_osdcolor=i;
656 free(zr->device);
657 zr->device = malloc(strlen(param)+1);
658 strcpy(zr->device, param);
659 mp_msg(MSGT_VO, MSGL_V, "zr: using device %s\n", zr->device);
660 return 1;
661 } else if (!strcasecmp(opt, "zrbw")) {
662 if (param != NULL) {
663 return ERR_OUT_OF_RANGE;
665 zr->bw = 1;
666 return 1;
667 } else if (!strcasecmp(opt, "zrfd")) {
668 if (param != NULL) {
669 return ERR_OUT_OF_RANGE;
671 zr->fd = 1;
672 return 1;
673 } else if (!strcasecmp(opt, "zrcrop")){
674 geo_t *g = &zr->g;
675 if (g->set == 1) {
676 zr_parsing++;
677 zr_count++;
678 zr = &zr_info[zr_parsing];
679 g = &zr->g;
680 if (zr_count > 4) {
681 mp_msg(MSGT_VO, MSGL_ERR, "zr: too many simultaneus display devices requested (max. is 4)\n");
682 return ERR_OUT_OF_RANGE;
685 if (param == NULL) return ERR_MISSING_PARAM;
686 if (sscanf(param, "%dx%d+%d+%d", &g->width, &g->height,
687 &g->xoff, &g->yoff) != 4) {
688 g->xoff = 0; g->yoff = 0;
689 if (sscanf(param, "%dx%d", &g->width, &g->height) != 2) {
690 mp_msg(MSGT_VO, MSGL_ERR, "zr: argument to -zrcrop must be of the form 352x288+16+0\n");
691 return ERR_OUT_OF_RANGE;
694 g->set = 1;
695 mp_msg(MSGT_VO, MSGL_V, "zr: cropping %s\n", param);
696 return 1;
697 }else if (!strcasecmp(opt, "zrhdec")) {
698 i = atoi(param);
699 if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE;
700 zr->hdec = i;
701 return 1;
702 }else if (!strcasecmp(opt, "zrbsize")) {
703 i = atoi(param);
704 if (i < 32) return ERR_OUT_OF_RANGE;
705 zr->buffer_size = i;
706 return 1;
707 }else if (!strcasecmp(opt, "zrvdec")) {
708 i = atoi(param);
709 if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE;
710 zr->vdec = i;
711 return 1;
712 }else if (!strcasecmp(opt, "zrxdoff")) {
713 i = atoi(param);
714 zr->xdoff = i;
715 return 1;
716 }else if (!strcasecmp(opt, "zrydoff")) {
717 i = atoi(param);
718 zr->ydoff = i;
719 return 1;
720 }else if (!strcasecmp(opt, "zrquality")) {
721 i = atoi(param);
722 if (i < 1 || i > 20) return ERR_OUT_OF_RANGE;
723 zr->quality = i;
724 return 1;
725 }else if (!strcasecmp(opt, "zrnorm")) {
726 if (param == NULL) return ERR_MISSING_PARAM;
727 if (!strcasecmp(param, "NTSC")) {
728 mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to NTSC\n");
729 zr->norm = VIDEO_MODE_NTSC;
730 return 1;
731 } else if (!strcasecmp(param, "PAL")) {
732 mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to PAL\n");
733 zr->norm = VIDEO_MODE_PAL;
734 return 1;
735 } else {
736 return ERR_OUT_OF_RANGE;
738 }else if (!strcasecmp(opt, "zrhelp")){
739 printf("Help for -vo zr: Zoran ZR360[56]7/ZR36060 based MJPEG capture/playback cards\n");
740 printf("\n");
741 printf("Here are the zr options:\n");
742 printf(
743 "\n"
744 " -zrcrop specify part of the input image that\n"
745 " you want to see as an x-style geometry string\n"
746 " example: -zrcrop 352x288+16+0\n"
747 " -zrvdec vertical decimation 1, 2 or 4\n"
748 " -zrhdec horizontal decimation 1, 2 or 4\n"
749 " -zrfd decimation is only done if the primitive\n"
750 " hardware upscaler can correct for the decimation,\n"
751 " this switch allows you to see the effects\n"
752 " of too much decimation\n"
753 " -zrbw display in black&white (speed increase)\n"
754 " -zrxdoff x offset from upper-left of TV screen (default is 'centered')\n"
755 " -zrydoff y offset from upper-left of TV screen (default is 'centered')\n"
756 " -zrquality jpeg compression quality [BEST] 1 - 20 [VERY BAD]\n"
757 " -zrdev playback device (example -zrdev /dev/video1)\n"
758 " -zrnorm specify norm PAL/NTSC (default: leave at current setting)\n"
759 " -zrbsize set the MPJEG buffer size to a number of kilobytes (def. 128)\n"
760 " use this if MPlayer complains about the MJPEG buffer size\n"
761 " being too small, 256kB is recommended. If your card/mobo\n"
762 " doesn't allow buffers > 128kB lower the jpeg encoding\n"
763 " quality or the resolution of the movie\n"
764 "\n"
765 "Cinerama support: additional occurances of -zrcrop activate cinerama mode,\n"
766 "suppose you have a 704x272 movie, two DC10+ cards and two beamers (or tv's),\n"
767 "then you would issue the following command:\n\n"
768 "mplayer -vo zr -zrcrop 352x272+0+0 -zrdev /dev/video0 -zrcrop 352x272+352+0 \\\n"
769 " -zrdev /dev/video1 movie.avi\n\n"
770 "Options appearing after the second -zrcrop apply to the second card, it is\n"
771 "possible to dispay at a different jpeg quality or at different decimations.\n\n"
772 "The parameters -zrxdoff and -zrydoff can be used to align the two images.\n"
773 "The maximum number of zoran cards participating in cinerama is 4, so you can\n"
774 "build a 2x2 vidiwall. (untested for obvious reasons, the setup wit a buz and\n"
775 "a DC10+ (and no beamers) is tested, however)\n"
777 exit(0);
780 return ERR_NOT_AN_OPTION;
783 void vo_zr_revertoption(m_option_t* opt,char* param) {
785 zr_info_t *zr = &zr_info[1];
786 zr_count = 1;
787 zr_parsing = 0;
789 if (!strcasecmp(param, "zrdev")) {
790 if(zr->device)
791 free(zr->device);
792 zr->device=NULL;
793 } else if (!strcasecmp(param, "zrbw"))
794 zr->bw=0;
795 else if (!strcasecmp(param, "zrfd"))
796 zr->fd=0;
797 else if (!strcasecmp(param, "zrcrop"))
798 zr->g.set = zr->g.xoff = zr->g.yoff = 0;
799 else if (!strcasecmp(param, "zrbsize"))
800 zr->buffer_size = 128;
801 else if (!strcasecmp(param, "zrhdec"))
802 zr->hdec = 1;
803 else if (!strcasecmp(param, "zrvdec"))
804 zr->vdec = 1;
805 else if (!strcasecmp(param, "zrxdoff"))
806 zr->xdoff = -1;
807 else if (!strcasecmp(param, "zrydoff"))
808 zr->ydoff = -1;
809 else if (!strcasecmp(param, "zrquality"))
810 zr->quality = 2;
811 else if (!strcasecmp(param, "zrnorm"))
812 zr->norm = VIDEO_MODE_AUTO;
816 static uint32_t preinit(const char *arg)
818 if(arg)
820 printf("vo_zr: Unknown subdevice: %s\n",arg);
821 return ENOSYS;
823 return 0;
826 static uint32_t control(uint32_t request, void *data, ...)
828 switch (request) {
829 case VOCTRL_QUERY_FORMAT:
830 return query_format(*((uint32_t*)data));
832 return VO_NOTIMPL;