Mercurial > mplayer.hg
changeset 6932:56cb4837384b
cinerama support in -vo zr for people with more than one zoran card, documentation contained in -zrhelp
author | rik |
---|---|
date | Mon, 05 Aug 2002 20:03:22 +0000 |
parents | da4a3a8e1f08 |
children | 4aa18bab0544 |
files | libvo/vo_zr.c |
diffstat | 1 files changed, 472 insertions(+), 369 deletions(-) [+] |
line wrap: on
line diff
--- a/libvo/vo_zr.c Mon Aug 05 19:38:30 2002 +0000 +++ b/libvo/vo_zr.c Mon Aug 05 20:03:22 2002 +0000 @@ -40,61 +40,77 @@ "" }; +#define ZR_MAX_DEVICES 4 /* General variables */ -static int image_width; -static int image_height; -static int off_y, off_c, stride; /* for use by 'draw slice/frame' */ -static int framenum; -static int fields = 1; /* currently no interlacing */ -static int zrfd = 0; -static int bw = 0; /* if bw == 1, then display in black&white */ -static int vdec = 1; -static int hdec = 1; -static int size; -static int quality = 2; -static unsigned char *y_data, *u_data, *v_data; -static int y_stride, u_stride, v_stride; - typedef struct { int width; int height; int xoff; int yoff; int set; -} geo; -geo g = {0, 0, 0, 0, 0}; +} geo_t; + +static int zr_count = 1; +static int zr_parsing = 0; +static int framenum; -static uint8_t *image=NULL; -static uint8_t *buf=NULL; +typedef struct { + /* commandline args given for this device (and defaults) */ + int vdec, hdec; /* requested decimation 1,2,4 */ + int fd; /* force decimation */ + int xdoff, ydoff; /* offset from upperleft of screen + * default is 'centered' */ + int quality; /* jpeg quality 1=best, 20=bad */ + geo_t g; /* view window (zrcrop) */ + char *device; /* /dev/video1 */ + int bw; /* if bw == 1, display in black&white */ + int norm; /* PAL/NTSC */ -static jpeg_enc_t *j; + /* buffers + pointers + info */ + + unsigned char *image; + int image_width, image_height, size; + int off_y, off_c, stride; /* for use by 'draw slice/frame' */ -/* Variables needed for Zoran */ + unsigned char *buf; /* the jpeg images will be placed here */ + jpeg_enc_t *j; + unsigned char *y_data, *u_data, *v_data; /* used by the jpeg encoder */ + int y_stride, u_stride, v_stride; /* these point somewhere in image */ + + /* information for (and about) the zoran card */ -int vdes; /* the file descriptor of the video device */ -int frame = 0, synco = 0, queue = 0; /* buffer management */ -struct mjpeg_params zp; -struct mjpeg_requestbuffers zrq; -struct mjpeg_sync zs; -struct video_capability vc; + int vdes; /* file descriptor of card */ + int frame, synco, queue; /* buffer management */ + struct mjpeg_sync zs; /* state information */ + struct mjpeg_params p; + struct video_capability vc; /* max resolution and so on */ + int fields, stretchy; /* must the *image be interlaced + or stretched to fit on the screen? */ +} zr_info_t; + +static zr_info_t zr_info[ZR_MAX_DEVICES] = { + {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0, + 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0, + 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0, + 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL, 0, VIDEO_MODE_AUTO, NULL, 0, 0, 0, 0, 0, + 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + + + + #define MJPEG_NBUFFERS 2 #define MJPEG_SIZE 1024*256 -//should be command line options -int norm = VIDEO_MODE_AUTO; -#if 0 -#ifndef VO_ZR_DEFAULT_DEVICE -#define VO_ZR_DEFAULT_DEVICE "/dev/video" -#endif -#endif -char *device = NULL; -int zoran_getcap() { +int zoran_getcap(zr_info_t *zr) { char* dev; - if (device) - dev = device; + if (zr->device) + dev = zr->device; else { /* code borrowed from mjpegtools lavplay.c // 20020416 too */ struct stat vstat; @@ -126,9 +142,9 @@ mp_msg(MSGT_VO, MSGL_V, "zr: found video device %s\n", dev); } - vdes = open(dev, O_RDWR); + zr->vdes = open(dev, O_RDWR); - if (vdes < 0) { + if (zr->vdes < 0) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error opening %s: %s\n", dev, strerror(errno)); return 1; @@ -137,61 +153,72 @@ /* before we can ask for the maximum resolution, we must set * the correct tv norm */ - if (ioctl(vdes, MJPIOC_G_PARAMS, &zp) < 0) { + if (ioctl(zr->vdes, MJPIOC_G_PARAMS, &zr->p) < 0) { mp_msg(MSGT_VO, MSGL_ERR, "zr: device at %s is probably not a DC10(+)/buz/lml33\n", dev); return 1; } - if (zp.norm != norm && norm != VIDEO_MODE_AUTO) { + if (zr->p.norm != zr->norm && zr->norm != VIDEO_MODE_AUTO) { /* attempt to set requested norm */ - zp.norm = norm; - if (ioctl(vdes, MJPIOC_S_PARAMS, &zp) < 0) { + zr->p.norm = zr->norm; + if (ioctl(zr->vdes, MJPIOC_S_PARAMS, &zr->p) < 0) { mp_msg(MSGT_VO, MSGL_ERR, "zr: unable to change video norm, use another program to change it (XawTV)\n"); return 1; } - ioctl(vdes, MJPIOC_G_PARAMS, &zp); - if (norm != zp.norm) { + ioctl(zr->vdes, MJPIOC_G_PARAMS, &zr->p); + if (zr->norm != zr->p.norm) { mp_msg(MSGT_VO, MSGL_ERR, "zr: unable to change video norm, use another program to change it (XawTV)\n"); return 1; } } - if (ioctl(vdes, VIDIOCGCAP, &vc) < 0) { + if (ioctl(zr->vdes, VIDIOCGCAP, &zr->vc) < 0) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error getting video capabilities from %s\n"); return 1; } - mp_msg(MSGT_VO, MSGL_V, "zr: MJPEG card reports maxwidth=%d, maxheight=%d\n", vc.maxwidth, vc.maxheight); + mp_msg(MSGT_VO, MSGL_V, "zr: MJPEG card reports maxwidth=%d, maxheight=%d\n", zr->vc.maxwidth, zr->vc.maxheight); return 0; } -int init_zoran(int zrhdec, int zrvdec) { +int init_zoran(zr_info_t *zr, int stretchx, int stretchy) { + struct mjpeg_requestbuffers zrq; /* center the image, and stretch it as far as possible (try to keep * aspect) and check if it fits */ - if (image_width > vc.maxwidth) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too wide, max width currenty %d\n", vc.maxwidth); + if (zr->image_width > zr->vc.maxwidth) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too wide, max width currenty %d\n", zr->vc.maxwidth); return 1; } - if (image_height > vc.maxheight) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too high, max height currenty %d\n", vc.maxheight); + if (zr->image_height > zr->vc.maxheight) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: movie to be played is too high, max height currenty %d\n", zr->vc.maxheight); return 1; } - zp.decimation = 0; - zp.HorDcm = zrhdec; - zp.VerDcm = zrvdec; - zp.TmpDcm = 1; - zp.field_per_buff = fields; - zp.img_x = (vc.maxwidth - zp.HorDcm*(int)image_width/hdec)/2; - zp.img_y = (vc.maxheight - zp.VerDcm*(3-fields)*(int)image_height)/4; - zp.img_width = zp.HorDcm*image_width/hdec; - zp.img_height = zp.VerDcm*image_height/fields; - mp_msg(MSGT_VO, MSGL_V, "zr: geometry (after 'scaling'): %dx%d+%d+%d fields=%d, w=%d, h=%d\n", zp.img_width, (3-fields)*zp.img_height, zp.img_x, zp.img_y, fields, image_width/hdec, image_height); + zr->p.decimation = 0; + zr->p.HorDcm = stretchx; + zr->p.VerDcm = stretchy; + zr->p.TmpDcm = 1; + zr->p.field_per_buff = zr->fields; + if (zr->xdoff == -1) { + zr->p.img_x = (zr->vc.maxwidth - + zr->p.HorDcm*(int)zr->image_width/zr->hdec)/2; + } else { + zr->p.img_x = zr->xdoff; + } + if (zr->ydoff == -1) { + zr->p.img_y = (zr->vc.maxheight - zr->p.VerDcm* + (3-zr->fields)*(int)zr->image_height)/4; + } else { + zr->p.img_y = zr->ydoff; + } + zr->p.img_width = zr->p.HorDcm*zr->image_width/zr->hdec; + zr->p.img_height = zr->p.VerDcm*zr->image_height/zr->fields; + 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); - if (ioctl(vdes, MJPIOC_S_PARAMS, &zp) < 0) { + if (ioctl(zr->vdes, MJPIOC_S_PARAMS, &zr->p) < 0) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error setting display parameters\n"); return 1; } @@ -199,234 +226,250 @@ zrq.count = MJPEG_NBUFFERS; zrq.size = MJPEG_SIZE; - if (ioctl(vdes, MJPIOC_REQBUFS, &zrq)) { + if (ioctl(zr->vdes, MJPIOC_REQBUFS, &zrq)) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error requesting %d buffers of size %d\n", zrq.count, zrq.size); return 1; } - buf = (char*)mmap(0, zrq.count*zrq.size, PROT_READ|PROT_WRITE, - MAP_SHARED, vdes, 0); + zr->buf = (unsigned char*)mmap(0, zrq.count*zrq.size, + PROT_READ|PROT_WRITE, MAP_SHARED, zr->vdes, 0); - if (buf == MAP_FAILED) { + if (zr->buf == MAP_FAILED) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error requesting %d buffers of size %d\n", zrq.count, zrq.size); return 1; } return 0; } -void uninit_zoran(void) { - if (image) { - free(image); - image=NULL; +void uninit_zoran(zr_info_t *zr) { + if (zr->image) { + free(zr->image); + zr->image=NULL; } - while (queue > synco + 1) { - if (ioctl(vdes, MJPIOC_SYNC, &zs) < 0) + while (zr->queue > zr->synco + 1) { + if (ioctl(zr->vdes, MJPIOC_SYNC, &zr->zs) < 0) mp_msg(MSGT_VO, MSGL_ERR, "zr: error waiting for buffers to become free\n"); - synco++; + zr->synco++; } /* stop streaming */ - frame = -1; - if (ioctl(vdes, MJPIOC_QBUF_PLAY, &frame) < 0) + zr->frame = -1; + if (ioctl(zr->vdes, MJPIOC_QBUF_PLAY, &zr->frame) < 0) mp_msg(MSGT_VO, MSGL_ERR, "zr: error stopping playback of last frame\n"); - close(vdes); + close(zr->vdes); } +int zr_geometry_sane(geo_t *g, unsigned int width, unsigned int height) { + if (g->set) { + if (g->width%2 != 0 || g->height%2 != 0 || + g->xoff%2 != 0 || g->yoff%2 != 0) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: arguments in -zrcrop must be multiples of 2\n"); + return 1; + } + if (g->width <= 0 || g->height <= 0 || + g->xoff < 0 || g->yoff < 0) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: width and height must be positive and offset nonnegative\n"); + return 1; + } + if (g->width + g->xoff > width) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: width+xoffset (%d+%d>%d) is too big\n", g->width, g->xoff, width); + return 1; + } + if (g->height + g->yoff > height) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: height+yoffset (%d+%d>%d) is too big\n", g->height, g->yoff, height); + return 1; + } + } else { + g->width = width; + g->height = height; + g->xoff = 0; + g->yoff = 0; + g->set = 1; + } + return 0; +} + + static uint32_t config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t fullscreen, char *title, uint32_t format,const vo_tune_info_t *info) { - int i, stretchx, stretchy; - /* this allows to crop parts from incoming picture, - * for easy 512x240 -> 352x240 */ - /* These values must be multples of 2 */ + int i, tmp, stretchx, stretchy; + framenum = 0; if (format != IMGFMT_YV12 && format != IMGFMT_YUY2) { printf("vo_zr called with wrong format"); exit(1); } - stride = 2*width; - if (g.set) { - if (g.width%2 != 0 || g.height%2 != 0 || - g.xoff%2 != 0 || g.yoff%2 != 0) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: arguments in -zrcrop must be multiples of 2\n"); - return 1; + for (i = 0; i < zr_count; i++) { + zr_info_t *zr = &zr_info[i]; + geo_t *g = &zr->g; + + zr->stride = 2*width; + if (zr_geometry_sane(g, width, height)) return 1; + + /* we must know the maximum resolution of the device + * it differs for DC10+ and buz for example */ + zoran_getcap(zr); /*must be called before init_zoran */ + /* make the scaling decision + * we are capable of stretching the image in the horizontal + * direction by factors 1, 2 and 4 + * we can stretch the image in the vertical direction by a + * factor of 1 and 2 AND we must decide about interlacing */ + if (g->width > zr->vc.maxwidth/2 || + g->height > zr->vc.maxheight/2) { + stretchx = 1; + stretchy = 1; + zr->fields = 2; + if (zr->vdec == 2) { + zr->fields = 1; + } else if (zr->vdec == 4) { + zr->fields = 1; + stretchy = 2; + } + stretchx = zr->hdec; + } else if (g->width > zr->vc.maxwidth/4 || + g->height > zr->vc.maxheight/4) { + stretchx = 2; + stretchy = 1; + zr->fields = 1; + if (zr->vdec == 2) { + stretchy = 2; + } else if (zr->vdec == 4) { + if (!zr->fd) { + mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 2 (use -zrfd to keep vdec=4)\n"); + zr->vdec = 2; + } + stretchy = 2; + } + if (zr->hdec == 2) { + stretchx = 4; + } else if (zr->hdec == 4){ + if (!zr->fd) { + mp_msg(MSGT_VO, MSGL_WARN, "zr: horizontal decimation too high, changing to 2 (use -zrfd to keep hdec=4)\n"); + zr->hdec = 2; + } + stretchx = 4; + } + } else { + /* output image is maximally stretched */ + stretchx = 4; + stretchy = 2; + zr->fields = 1; + if (zr->vdec != 1 && !zr->fd) { + mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep vdec=%d)\n", zr->vdec); + zr->vdec = 1; + } + if (zr->hdec != 1 && !zr->fd) { + mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep hdec=%d)\n", zr->hdec); + zr->hdec = 1; + } } - if (g.width <= 0 || g.height <= 0 || - g.xoff < 0 || g.yoff < 0) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: width and height must be positive and offset nonnegative\n"); - return 1; + /* It can be that the original frame was too big for display, + * or that the width of the decimated image (for example) after + * padding up to a multiple of 16 has become too big. (orig + * width 720 (exactly right for the Buz) after decimation 360, + * after padding up to a multiple of 16 368, display 736 -> too + * large). In these situations we auto(re)crop. */ + tmp = 16*((g->width - 1)/(zr->hdec*16) + 1); + if (stretchx*tmp > zr->vc.maxwidth) { + g->xoff += 2*((g->width - zr->hdec*(tmp-16))/4); + /* g->off must be a multiple of 2 */ + g->width = zr->hdec*(tmp - 16); + g->set = 0; /* we abuse this field to + report that g has changed*/ + } + tmp = 8*zr->fields*((g->height - 1)/(zr->vdec*zr->fields*8)+1); + if (stretchy*tmp > zr->vc.maxheight) { + g->yoff += 2*((g->height - zr->vdec* + (tmp - 8*zr->fields))/4); + g->height = zr->vdec*(tmp - 8*zr->fields); + g->set = 0; } - if (g.width + g.xoff > width) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: width+xoffset (%d+%d>%d) is too big\n", g.width, g.xoff, width); - return 1; - } - if (g.height + g.yoff > height) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: height+yoffset (%d+%d>%d) is too big\n", g.height, g.yoff, height); + if (!g->set) + 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); + + /* the height must be a multiple of fields*8 and the width + * must be a multiple of 16 */ + /* add some black borders to make it so, and center the image*/ + zr->image_height = zr->fields*8*((g->height/zr->vdec - 1)/ + (zr->fields*8) + 1); + zr->image_width = (zr->hdec*16)*((g->width - 1)/(zr->hdec*16) + 1); + zr->off_y = (zr->image_height - g->height/zr->vdec)/2; + if (zr->off_y%2 != 0) zr->off_y++; + zr->off_y *= zr->image_width; + zr->off_c = zr->off_y/4; + zr->off_y += (zr->image_width - g->width)/2; + if (zr->off_y%2 != 0) zr->off_y--; + zr->off_c += (zr->image_width - g->width)/4; + zr->size = zr->image_width*zr->image_height; + 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); + + zr->image = malloc(2*zr->size); /* this buffer allows for YUV422 data, + * so it is a bit too big for YUV420 */ + if (!zr->image) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: Memory exhausted\n"); return 1; } - } else { - g.width = width; - g.height = height; - g.xoff = 0; - g.yoff = 0; - g.set = 1; - } - /* we must know the maximum resolution of the device - * it differs for DC10+ and buz for example */ - zoran_getcap(); /*must be called before init_zoran */ - /* make the scaling decision - * we are capable of stretching the image in the horizontal - * direction by factors 1, 2 and 4 - * we can stretch the image in the vertical direction by a factor - * of 1 and 2 AND we must decide about interlacing */ - if (g.width > vc.maxwidth/2 || g.height > vc.maxheight/2) { - stretchx = 1; - stretchy = 1; - fields = 2; - if (vdec == 2) { - fields = 1; - } else if (vdec == 4) { - fields = 1; - stretchy = 2; - } - stretchx = hdec; - } else if (g.width > vc.maxwidth/4 || g.height > vc.maxheight/4) { - stretchx = 2; - stretchy = 1; - fields = 1; - if (vdec == 2) { - stretchy = 2; - } else if (vdec == 4) { - if (!zrfd) { - mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 2 (use -zrfd to keep vdec=4)\n"); - vdec = 2; - } - stretchy = 2; + /* and make sure that the borders are _really_ black */ + switch (format) { + case IMGFMT_YV12: + memset(zr->image, 0, zr->size); + memset(zr->image + zr->size, 0x80, zr->size/4); + memset(zr->image + 3*zr->size/2, 0x80, zr->size/4); + zr->y_data = zr->image; + zr->u_data = zr->image + zr->size; + zr->v_data = zr->image + 3*zr->size/2; + + zr->y_stride = zr->image_width; + zr->u_stride = zr->image_width/2; + zr->v_stride = zr->image_width/2; + + zr->j = jpeg_enc_init(zr->image_width/zr->hdec, + zr->image_height/zr->fields, + zr->hdec, zr->y_stride*zr->fields, + zr->hdec, zr->u_stride*zr->fields, + zr->hdec, zr->v_stride*zr->fields, + 1, zr->quality, zr->bw); + break; + case IMGFMT_YUY2: + for (tmp = 0; tmp < 2*zr->size; tmp+=4) { + zr->image[tmp] = 0; + zr->image[tmp+1] = 0x80; + zr->image[tmp+2] = 0; + zr->image[tmp+3] = 0x80; + } + + zr->y_data = zr->image; + zr->u_data = zr->image + 1; + zr->v_data = zr->image + 3; + + zr->y_stride = 2*zr->image_width; + zr->u_stride = 2*zr->image_width; + zr->v_stride = 2*zr->image_width; + + zr->j = jpeg_enc_init(zr->image_width/zr->hdec, + zr->image_height/zr->fields, + zr->hdec*2, + zr->y_stride*zr->fields, + zr->hdec*4, + zr->u_stride*zr->fields, + zr->hdec*4, + zr->v_stride*zr->fields, + 0, zr->quality, zr->bw); + break; + default: + mp_msg(MSGT_VO, MSGL_FATAL, "zr: internal inconsistency in vo_zr\n"); } - if (hdec == 2) { - stretchx = 4; - } else if (hdec == 4){ - if (!zrfd) { - mp_msg(MSGT_VO, MSGL_WARN, "zr: horizontal decimation too high, changing to 2 (use -zrfd to keep hdec=4)\n"); - hdec = 2; - } - stretchx = 4; - } - } else { - /* output image is maximally stretched */ - stretchx = 4; - stretchy = 2; - fields = 1; - if (vdec != 1 && !zrfd) { - mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep vdec=%d)\n", vdec); - vdec = 1; + + + if (zr->j == NULL) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: error initializing the jpeg encoder\n"); + return 1; } - - if (hdec != 1 && !zrfd) { - mp_msg(MSGT_VO, MSGL_WARN, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep hdec=%d)\n", hdec); - hdec = 1; + + if (init_zoran(zr, stretchx, stretchy)) { + return 1; } - } - /* It can be that the original frame was too big for display, - * or that the width of the decimated image (for example) after - * padding up to a multiple of 16 has become too big. (orig - * width 720 (exactly right for the Buz) after decimation 360, - * after padding up to a multiple of 16 368, display 736 -> too - * large). In these situations we auto(re)crop. */ - i = 16*((g.width - 1)/(hdec*16) + 1); - if (stretchx*i > vc.maxwidth) { - g.xoff += 2*((g.width - hdec*(i-16))/4); - /* g.off must be a multiple of 2 */ - g.width = hdec*(i - 16); - g.set = 0; /* we abuse this field to report that g has changed*/ - } - i = 8*fields*((g.height - 1)/(vdec*fields*8) + 1); - if (stretchy*i > vc.maxheight) { - g.yoff += 2*((g.height - vdec*(i - 8*fields))/4); - g.height = vdec*(i - 8*fields); - g.set = 0; + } - if (!g.set) - 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); - - /* the height must be a multiple of fields*8 and the width - * must be a multiple of 16 */ - /* add some black borders to make it so, and center the image*/ - image_height = fields*8*((g.height/vdec - 1)/(fields*8) + 1); - image_width = (hdec*16)*((g.width - 1)/(hdec*16) + 1); - off_y = (image_height - g.height/vdec)/2; - if (off_y%2 != 0) off_y++; - off_y *= image_width; - off_c = off_y/4; - off_y += (image_width - g.width)/2; - if (off_y%2 != 0) off_y--; - off_c += (image_width - g.width)/4; - framenum = 0; - size = image_width*image_height; - 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, image_width, image_height, off_y, off_c); - - image = malloc(2*size); /* this buffer allows for YUV422 data, - * so it is a bit too big for YUV420 */ - if (!image) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: Memory exhausted\n"); - return 1; - } - /* and make sure that the borders are _really_ black */ - switch (format) { - case IMGFMT_YV12: - memset(image, 0, image_width*image_height); - memset(image + size, 0x80, image_width*image_height/4); - memset(image + 3*size/2, 0x80, image_width*image_height/4); - y_data = image; - u_data = image + image_width*image_height; - v_data = image + 3*image_width*image_height/2; - - y_stride = image_width; - u_stride = image_width/2; - v_stride = image_width/2; - - j = jpeg_enc_init(image_width/hdec, - image_height/fields, - hdec, y_stride*fields, - hdec, u_stride*fields, - hdec, v_stride*fields, - 1, quality, bw); - break; - case IMGFMT_YUY2: - for (i = 0; i < 2*size; i+=4) { - image[i] = 0; - image[i+1] = 0x80; - image[i+2] = 0; - image[i+3] = 0x80; - } - - y_data = image; - u_data = image + 1; - v_data = image + 3; - - y_stride = 2*image_width; - u_stride = 2*image_width; - v_stride = 2*image_width; - - j = jpeg_enc_init(image_width/hdec, - image_height/fields, - hdec*2, y_stride*fields, - hdec*4, u_stride*fields, - hdec*4, v_stride*fields, - 0, quality, bw); - break; - default: - mp_msg(MSGT_VO, MSGL_FATAL, "zr: internal inconsistency in vo_zr\n"); - } - - - if (j == NULL) { - mp_msg(MSGT_VO, MSGL_ERR, "zr: error initializing the jpeg encoder\n"); - return 1; - } - - if (init_zoran(stretchx, stretchy)) { - return 1; - } - return 0; } @@ -438,23 +481,27 @@ } static void flip_page (void) { - int i, k; + int i, j, k; //FILE *fp; //char filename[100]; /* do we have a free buffer? */ - if (queue-synco < zrq.count) { - frame = queue; - } else { - if (ioctl(vdes, MJPIOC_SYNC, &zs) < 0) - mp_msg(MSGT_VO, MSGL_ERR, "zr: error waiting for buffers to become free\n"); - frame = zs.frame; - synco++; + for (j = 0; j < zr_count; j++) { + zr_info_t *zr = &zr_info[j]; + if (zr->queue-zr->synco < MJPEG_NBUFFERS) { + zr->frame = zr->queue; + } else { + if (ioctl(zr->vdes, MJPIOC_SYNC, &zr->zs) < 0) + mp_msg(MSGT_VO, MSGL_ERR, "zr: error waiting for buffers to become free\n"); + zr->frame = zr->zs.frame; + zr->synco++; + } + k=0; + for (i = 0; i < zr->fields; i++) + k+=jpeg_enc_frame(zr->j, zr->y_data + i*zr->y_stride, + zr->u_data + i*zr->u_stride, + zr->v_data + i*zr->v_stride, + zr->buf+zr->frame*MJPEG_SIZE+k); } - k=0; - for (i = 0; i < fields; i++) - k+=jpeg_enc_frame(j, y_data + i*y_stride, - u_data + i*u_stride, v_data + i*v_stride, - buf+frame*zrq.size+k); /* Warning: Only the first jpeg image contains huffman- and * quantisation tables, so don't expect files other than * test0001.jpg to be readable */ @@ -467,25 +514,31 @@ fread(buf+frame*zrq.size, 1, 2126, fp); fclose(fp);*/ - if (ioctl(vdes, MJPIOC_QBUF_PLAY, &frame) < 0) - mp_msg(MSGT_VO, MSGL_ERR, - "zr: error queueing buffer for playback\n"); - queue++; + for (j = 0; j < zr_count; j++) { + zr_info_t *zr = &zr_info[j]; + if (ioctl(zr->vdes, MJPIOC_QBUF_PLAY, &zr->frame) < 0) + mp_msg(MSGT_VO, MSGL_ERR, "zr: error queueing buffer for playback\n"); + zr->queue++; + } framenum++; return; } static uint32_t draw_frame(uint8_t * src[]) { - int i; + int i, j; char *source, *dest; //printf("draw frame called\n"); - source = src[0] + 2*g.yoff*vdec*stride + 2*g.xoff; - dest = image + 2*off_y; - for (i = 0; i < g.height/vdec; i++) { - memcpy(dest, source, image_width*2); - dest += 2*image_width; - source += vdec*stride; + for (j = 0; j < zr_count; j++) { + zr_info_t *zr = &zr_info[j]; + geo_t *g = &zr->g; + source = src[0] + 2*g->yoff*zr->vdec*zr->stride + 2*g->xoff; + dest = zr->image + 2*zr->off_y; + for (i = 0; i < g->height/zr->vdec; i++) { + memcpy(dest, source, zr->image_width*2); + dest += 2*zr->image_width; + source += zr->vdec*zr->stride; + } } return 0; } @@ -497,8 +550,12 @@ } static void uninit(void) { - jpeg_enc_uninit(j); - uninit_zoran(); + int j; + mp_msg(MSGT_VO, MSGL_V, "zr: uninit called\n"); + for (j = 0; j < zr_count; j++) { + jpeg_enc_uninit(zr_info[j].j); + uninit_zoran(&zr_info[j]); + } } static void check_events(void) { @@ -506,70 +563,75 @@ static uint32_t draw_slice(uint8_t *srcimg[], int stride[], - int w, int h, int x, int y) { - int i; + int wf, int hf, int xf, int yf) { + int i, j, w, h, x, y; /* Apply 'geometry', crop unwanted parts */ uint8_t *dst; - uint8_t *src; //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]); - if (x < g.xoff) { - srcimg[0] += g.xoff - x; - srcimg[1] += (g.xoff - x)/2; - srcimg[2] += (g.xoff - x)/2; - w -= g.xoff - x; - if (w < 0) return 0; - x = 0 /*g.xoff*/; - } else { - x -= g.xoff; - } - if (x + w > g.width) { - w = g.width - x; - if (w < 0) return 0; - } - if (y < g.yoff) { - srcimg[0] += (g.yoff - y)*stride[0]; - srcimg[1] += ((g.yoff - y)/2)*stride[1]; - srcimg[2] += ((g.yoff - y)/2)*stride[2]; - h -= g.yoff - y; - if (h < 0) return 0; - y = 0; - } else { - y -= g.yoff; - } - if (y + h > g.height) { - h = g.height - y; - if (h < 0) return 0; - } - //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]); - dst=image + off_y + image_width*(y/vdec)+x; - src=srcimg[0]; - // copy Y: - for (i = 0; i < h; i++) { - if ((i + x)%vdec == 0) { - memcpy(dst,src,w); - dst+=image_width; - } - src+=stride[0]; - - } - if (!bw) { - // copy U+V: + for (j = 0; j < zr_count; j++) { + uint8_t *src=srcimg[0]; uint8_t *src1=srcimg[1]; uint8_t *src2=srcimg[2]; - uint8_t *dst1=image + size + off_c+ (y/(vdec*2))*image_width/2+(x/2); - uint8_t *dst2=image + 3*size/2 + off_c + - (y/(vdec*2))*image_width/2+(x/2); - for (i = 0; i< h/2; i++) { - if ((i+x/2)%vdec == 0) { - memcpy(dst1,src1,w/2); - memcpy(dst2,src2,w/2); - dst1+=image_width/2; - dst2+=image_width/2; + zr_info_t *zr = &zr_info[j]; + geo_t *g = &zr->g; + w = wf; h = hf; x = xf; y = yf; + if (x < g->xoff) { + src += g->xoff - x; + src1 += (g->xoff - x)/2; + src2 += (g->xoff - x)/2; + w -= g->xoff - x; + if (w < 0) break; //return 0; + x = 0 /*g.xoff*/; + } else { + x -= g->xoff; + } + if (x + w > g->width) { + w = g->width - x; + if (w < 0) break; //return 0; + } + if (y < g->yoff) { + src += (g->yoff - y)*stride[0]; + src1 += ((g->yoff - y)/2)*stride[1]; + src2 += ((g->yoff - y)/2)*stride[2]; + h -= g->yoff - y; + if (h < 0) break; //return 0; + y = 0; + } else { + y -= g->yoff; + } + if (y + h > g->height) { + h = g->height - y; + if (h < 0) break; //return 0; + } + //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]); + dst=zr->image + zr->off_y + zr->image_width*(y/zr->vdec)+x; + // copy Y: + for (i = 0; i < h; i++) { + if ((i + x)%zr->vdec == 0) { + memcpy(dst,src,w); + dst+=zr->image_width; } - src1+=stride[1]; - src2+=stride[2]; + src+=stride[0]; + } - } + if (!zr->bw) { + // copy U+V: + uint8_t *dst1=zr->image + zr->size + zr->off_c+ (y/(zr->vdec*2))*zr->image_width/2+(x/2); + uint8_t *dst2=zr->image + 3*zr->size/2 + zr->off_c + + (y/(zr->vdec*2))* + zr->image_width/2+(x/2); + for (i = 0; i< h/2; i++) { + if ((i+x/2)%zr->vdec == 0) { + memcpy(dst1,src1,w/2); + memcpy(dst2,src2,w/2); + dst1+=zr->image_width/2; + dst2+=zr->image_width/2; + } + src1+=stride[1]; + src2+=stride[2]; + } + } + } return 0; } @@ -578,66 +640,86 @@ int vo_zr_parseoption(struct config * conf, char *opt, char *param){ /* got an option starting with zr */ - char *x, *help; + zr_info_t *zr = &zr_info[zr_parsing]; int i; /* do WE need it ?, always */ if (!strcasecmp(opt, "zrdev")) { if (param == NULL) return ERR_MISSING_PARAM; //if ((i=getcolor(param))==-1) return ERR_OUT_OF_RANGE; //aaopt_osdcolor=i; - device = malloc(strlen(param)+1); - strcpy(device, param); - mp_msg(MSGT_VO, MSGL_V, "zr: using device %s\n", device); + free(zr->device); + zr->device = malloc(strlen(param)+1); + strcpy(zr->device, param); + mp_msg(MSGT_VO, MSGL_V, "zr: using device %s\n", zr->device); return 1; } else if (!strcasecmp(opt, "zrbw")) { if (param != NULL) { return ERR_OUT_OF_RANGE; } - bw = 1; + zr->bw = 1; return 1; } else if (!strcasecmp(opt, "zrfd")) { if (param != NULL) { return ERR_OUT_OF_RANGE; } - zrfd = 1; + zr->fd = 1; return 1; } else if (!strcasecmp(opt, "zrcrop")){ + geo_t *g = &zr->g; + if (g->set == 1) { + zr_parsing++; + zr_count++; + zr = &zr_info[zr_parsing]; + g = &zr->g; + if (zr_count > 4) { + mp_msg(MSGT_VO, MSGL_ERR, "zr: too many simultaneus display devices requested (max. is 4)\n"); + return ERR_OUT_OF_RANGE; + } + } if (param == NULL) return ERR_MISSING_PARAM; - if (sscanf(param, "%dx%d+%d+%d", &g.width, &g.height, - &g.xoff, &g.yoff) != 4) { - g.xoff = 0; g.yoff = 0; - if (sscanf(param, "%dx%d", &g.width, &g.height) != 2) { + if (sscanf(param, "%dx%d+%d+%d", &g->width, &g->height, + &g->xoff, &g->yoff) != 4) { + g->xoff = 0; g->yoff = 0; + if (sscanf(param, "%dx%d", &g->width, &g->height) != 2) { mp_msg(MSGT_VO, MSGL_ERR, "zr: argument to -zrcrop must be of the form 352x288+16+0\n"); return ERR_OUT_OF_RANGE; } } - g.set = 1; + g->set = 1; mp_msg(MSGT_VO, MSGL_V, "zr: cropping %s\n", param); return 1; }else if (!strcasecmp(opt, "zrhdec")) { i = atoi(param); if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE; - hdec = i; + zr->hdec = i; return 1; }else if (!strcasecmp(opt, "zrvdec")) { i = atoi(param); if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE; - vdec = i; + zr->vdec = i; + return 1; + }else if (!strcasecmp(opt, "zrxdoff")) { + i = atoi(param); + zr->xdoff = i; + return 1; + }else if (!strcasecmp(opt, "zrydoff")) { + i = atoi(param); + zr->ydoff = i; return 1; }else if (!strcasecmp(opt, "zrquality")) { i = atoi(param); if (i < 1 || i > 20) return ERR_OUT_OF_RANGE; - quality = i; + zr->quality = i; return 1; }else if (!strcasecmp(opt, "zrnorm")) { if (param == NULL) return ERR_MISSING_PARAM; if (!strcasecmp(param, "NTSC")) { mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to NTSC\n"); - norm = VIDEO_MODE_NTSC; + zr->norm = VIDEO_MODE_NTSC; return 1; } else if (!strcasecmp(param, "PAL")) { mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to PAL\n"); - norm = VIDEO_MODE_PAL; + zr->norm = VIDEO_MODE_PAL; return 1; } else { return ERR_OUT_OF_RANGE; @@ -658,10 +740,23 @@ " this switch allows you to see the effects\n" " of too much decimation\n" " -zrbw display in black&white (speed increase)\n" + " -zrxdoff x offset from upper-left of TV screen (default is 'centered')\n" + " -zrydoff y offset from upper-left of TV screen (default is 'centered')\n" " -zrquality jpeg compression quality [BEST] 1 - 20 [VERY BAD]\n" - " -zrdev playback device (example -zrdev /dev/video1\n" - " -zrnorm specify norm PAL/NTSC [dev: leave at current setting]\n" + " -zrdev playback device (example -zrdev /dev/video1)\n" + " -zrnorm specify norm PAL/NTSC (default: leave at current setting)\n" "\n" + "Cinerama support: additional occurances of -zrcrop activate cinerama mode,\n" + "suppose you have a 704x272 movie, two DC10+ cards and two beamers (or tv's),\n" + "then you would issue the following command:\n\n" + "mplayer -vo zr -zrcrop 352x272+0+0 -zrdev /dev/video0 -zrcrop 352x272+352+0 \\\n" + " -zrdev /dev/video1 movie.avi\n\n" + "Options appearing after the second -zrcrop apply to the second card, it is\n" + "possible to dispay at a different jpeg quality or at different decimations.\n\n" + "The paramerters -zrxdoff and -zrydoff can be used to align the two images.\n" + "The maximum number of zoran cards participating in cinerama is 4, so you can\n" + "build a 2x2 vidiwall. (untested for obvious reasons, the setup wit a buz and\n" + "a DC10+ (and no beamers) is tested, however)\n" ); exit(0); @@ -671,24 +766,32 @@ void vo_zr_revertoption(config_t* opt,char* param) { + zr_info_t *zr = &zr_info[1]; + zr_count = 1; + zr_parsing = 0; + if (!strcasecmp(param, "zrdev")) { - if(device) - free(device); - device=NULL; + if(zr->device) + free(zr->device); + zr->device=NULL; } else if (!strcasecmp(param, "zrbw")) - bw=0; + zr->bw=0; else if (!strcasecmp(param, "zrfd")) - zrfd=0; + zr->fd=0; else if (!strcasecmp(param, "zrcrop")) - g.set = g.xoff = g.yoff = 0; + zr->g.set = zr->g.xoff = zr->g.yoff = 0; else if (!strcasecmp(param, "zrhdec")) - hdec = 1; + zr->hdec = 1; else if (!strcasecmp(param, "zrvdec")) - vdec = 1; + zr->vdec = 1; + else if (!strcasecmp(param, "zrxdoff")) + zr->vdec = -1; + else if (!strcasecmp(param, "zrydoff")) + zr->vdec = -1; else if (!strcasecmp(param, "zrquality")) - quality = 2; + zr->quality = 2; else if (!strcasecmp(param, "zrnorm")) - norm = VIDEO_MODE_AUTO; + zr->norm = VIDEO_MODE_AUTO; }