Mercurial > mplayer.hg
view libvo/vo_zr.c @ 7964:143d730908ae
here is a somewhat generic equalizer implementation for the X11 vo drivers
using the window's colormap (DirectColor).
this method is using the video card's hardware gamma ramp so it involves
no performance penalties at all.
patch by lucho <lucho@haemimont.bg>
author | arpi |
---|---|
date | Tue, 29 Oct 2002 20:27:47 +0000 |
parents | 1d2d47aa8717 |
children | 5b39e79af5fe |
line wrap: on
line source
/* * vo_zr.c - playback on zoran cards * Copyright (C) Rik Snel 2001,2002, License GNU GPL v2 */ /* $Id$ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/time.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <linux/types.h> #include <linux/videodev.h> #include "videodev_mjpeg.h" #include "config.h" #include "video_out.h" #include "video_out_internal.h" #include "../mp_msg.h" #include "../cfgparser.h" #include "fastmemcpy.h" #include "jpeg_enc.h" LIBVO_EXTERN (zr) static vo_info_t vo_info = { "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)", "zr", "Rik Snel <snel@phys.uu.nl>", "" }; #define ZR_MAX_DEVICES 4 /* General variables */ typedef struct { int width; int height; int xoff; int yoff; int set; } geo_t; static int zr_count = 1; static int zr_parsing = 0; static int framenum; 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 */ int buffer_size; /* MJPEG buffer size */ /* buffers + pointers + info */ unsigned char *image; int image_width, image_height, size; int off_y, off_c, stride; /* for use by 'draw slice/frame' */ 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; /* 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, 128, 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, 128, 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, 128, 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, 128, 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*128*/ int zoran_getcap(zr_info_t *zr) { char* dev; if (zr->device) dev = zr->device; else { /* code borrowed from mjpegtools lavplay.c // 20020416 too */ struct stat vstat; #undef VIDEV #define VIDEV "/dev/video" if (stat(VIDEV, &vstat) == 0 && S_ISCHR(vstat.st_mode)) dev = VIDEV; #undef VIDEV #define VIDEV "/dev/video0" else if (stat(VIDEV, &vstat) == 0 && S_ISCHR(vstat.st_mode)) dev = VIDEV; #undef VIDEV #define VIDEV "/dev/v4l/video0" else if (stat(VIDEV, &vstat) == 0 && S_ISCHR(vstat.st_mode)) dev = VIDEV; #undef VIDEV #define VIDEV "/dev/v4l0" else if (stat(VIDEV, &vstat) == 0 && S_ISCHR(vstat.st_mode)) dev = VIDEV; #undef VIDEV #define VIDEV "/dev/v4l" else if (stat(VIDEV, &vstat) == 0 && S_ISCHR(vstat.st_mode)) dev = VIDEV; #undef VIDEV else { mp_msg(MSGT_VO, MSGL_ERR, "zr: unable to find video device\n"); return 1; } mp_msg(MSGT_VO, MSGL_V, "zr: found video device %s\n", dev); } zr->vdes = open(dev, O_RDWR); if (zr->vdes < 0) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error opening %s: %s\n", dev, strerror(errno)); return 1; } /* before we can ask for the maximum resolution, we must set * the correct tv norm */ 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 (zr->p.norm != zr->norm && zr->norm != VIDEO_MODE_AUTO) { /* attempt to set requested norm */ 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(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(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", zr->vc.maxwidth, zr->vc.maxheight); return 0; } 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 (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 (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; } 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(zr->vdes, MJPIOC_S_PARAMS, &zr->p) < 0) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error setting display parameters\n"); return 1; } zrq.count = MJPEG_NBUFFERS; zrq.size = 1024*zr->buffer_size; 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; } zr->buf = (unsigned char*)mmap(0, zrq.count*zrq.size, PROT_READ|PROT_WRITE, MAP_SHARED, zr->vdes, 0); 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(zr_info_t *zr) { if (zr->image) { free(zr->image); zr->image=NULL; } 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"); zr->synco++; } /* stop streaming */ 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(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) { int i, tmp, stretchx, stretchy; framenum = 0; if (format != IMGFMT_YV12 && format != IMGFMT_YUY2) { printf("vo_zr called with wrong format"); exit(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; } } /* 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->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; } /* 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 (zr->j == NULL) { mp_msg(MSGT_VO, MSGL_ERR, "zr: error initializing the jpeg encoder\n"); return 1; } if (init_zoran(zr, stretchx, stretchy)) { return 1; } } return 0; } static const vo_info_t* get_info(void) { return &vo_info; } static void draw_osd(void) { } static void flip_page (void) { int i, j, k; //FILE *fp; //char filename[100]; /* do we have a free buffer? */ 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+ 1024*zr->frame*zr->buffer_size+k); 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"); } /* Warning: Only the first jpeg image contains huffman- and * quantisation tables, so don't expect files other than * test0001.jpg to be readable */ /*sprintf(filename, "test%04d.jpg", framenum); fp = fopen(filename, "w"); if (!fp) exit(1); fwrite(buf+frame*zrq.size, 1, k, fp); fclose(fp);*/ /*fp = fopen("test1.jpg", "r"); fread(buf+frame*zrq.size, 1, 2126, fp); fclose(fp);*/ 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, j; char *source, *dest; //printf("draw frame called\n"); 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; } static uint32_t query_format(uint32_t format) { if(format==IMGFMT_YV12 || format==IMGFMT_YUY2) return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW; return 0; } static void uninit(void) { 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) { } static uint32_t draw_slice(uint8_t *srcimg[], int stride[], int wf, int hf, int xf, int yf) { int i, j, w, h, x, y; /* Apply 'geometry', crop unwanted parts */ uint8_t *dst; //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]); for (j = 0; j < zr_count; j++) { uint8_t *src=srcimg[0]; uint8_t *src1=srcimg[1]; uint8_t *src2=srcimg[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; } 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; } /* copied and adapted from vo_aa_parseoption */ int vo_zr_parseoption(struct config * conf, char *opt, char *param){ /* got an option starting with zr */ 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; 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; } zr->bw = 1; return 1; } else if (!strcasecmp(opt, "zrfd")) { if (param != NULL) { return ERR_OUT_OF_RANGE; } 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) { 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; 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; zr->hdec = i; return 1; }else if (!strcasecmp(opt, "zrbsize")) { i = atoi(param); if (i < 32) return ERR_OUT_OF_RANGE; zr->buffer_size = i; return 1; }else if (!strcasecmp(opt, "zrvdec")) { i = atoi(param); if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE; 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; 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"); zr->norm = VIDEO_MODE_NTSC; return 1; } else if (!strcasecmp(param, "PAL")) { mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to PAL\n"); zr->norm = VIDEO_MODE_PAL; return 1; } else { return ERR_OUT_OF_RANGE; } }else if (!strcasecmp(opt, "zrhelp")){ printf("Help for -vo zr: Zoran ZR360[56]7/ZR36060 based MJPEG capture/playback cards\n"); printf("\n"); printf("Here are the zr options:\n"); printf( "\n" " -zrcrop specify part of the input image that\n" " you want to see as an x-style geometry string\n" " example: -zrcrop 352x288+16+0\n" " -zrvdec vertical decimation 1, 2 or 4\n" " -zrhdec horizontal decimation 1, 2 or 4\n" " -zrfd decimation is only done if the primitive\n" " hardware upscaler can correct for the decimation,\n" " 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 (default: leave at current setting)\n" " -zrbsize set the MPJEG buffer size to a number of kilobytes (def. 128)\n" " use this if MPlayer complains about the MJPEG buffer size\n" " being too small, 256kB is recommended. If your card/mobo\n" " doesn't allow buffers > 128kB lower the jpeg encoding\n" " quality or the resolution of the movie\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 parameters -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); } return ERR_NOT_AN_OPTION; } 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(zr->device) free(zr->device); zr->device=NULL; } else if (!strcasecmp(param, "zrbw")) zr->bw=0; else if (!strcasecmp(param, "zrfd")) zr->fd=0; else if (!strcasecmp(param, "zrcrop")) zr->g.set = zr->g.xoff = zr->g.yoff = 0; else if (!strcasecmp(param, "zrbsize")) zr->buffer_size = 128; else if (!strcasecmp(param, "zrhdec")) zr->hdec = 1; else if (!strcasecmp(param, "zrvdec")) zr->vdec = 1; else if (!strcasecmp(param, "zrxdoff")) zr->xdoff = -1; else if (!strcasecmp(param, "zrydoff")) zr->ydoff = -1; else if (!strcasecmp(param, "zrquality")) zr->quality = 2; else if (!strcasecmp(param, "zrnorm")) zr->norm = VIDEO_MODE_AUTO; } static uint32_t preinit(const char *arg) { if(arg) { printf("vo_zr: Unknown subdevice: %s\n",arg); return ENOSYS; } return 0; } static uint32_t control(uint32_t request, void *data, ...) { switch (request) { case VOCTRL_QUERY_FORMAT: return query_format(*((uint32_t*)data)); } return VO_NOTIMPL; }