1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-12 09:56:30 +00:00
mpv/libvo/vo_zr.c
rik daf51c3049 proper fix for 'some chipsets can't handle buffers > 128kB', vo_zr used
to ignore information about the _actual_ buffer size returned by the
kernel and continued to use the requested buffer size (the old fix was
to request smaller buffers, but not ignoring information about the
actual buffer size is way better)


git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@11377 b3059339-0415-0410-9bf9-f77b7e298cf2
2003-11-03 17:38:50 +00:00

827 lines
25 KiB
C

/*
* 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 "../m_option.h"
#include "fastmemcpy.h"
#include "jpeg_enc.h"
static vo_info_t info =
{
"Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
"zr",
"Rik Snel <snel@phys.uu.nl>",
""
};
LIBVO_EXTERN (zr)
#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 */
/* 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 mjpeg_requestbuffers zrq;
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, 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, 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, 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, 0, 0}};
#define MJPEG_NBUFFERS 2
#define MJPEG_SIZE 1024*256
int zoran_getcap(zr_info_t *zr) {
char* dev = NULL;
if (zr->device)
dev = zr->device;
else {
struct stat vstat;
const char *devs[] = {
"/dev/video",
"/dev/video0",
"/dev/v4l/video0",
"/dev/v4l0",
"/dev/v4l",
NULL
};
int i = 0;
do
{
if ((stat(devs[i], &vstat) == 0) && S_ISCHR(vstat.st_mode))
{
dev = devs[i];
mp_msg(MSGT_VO, MSGL_V, "zr: found video device %s\n", dev);
break;
}
} while (devs[++i] != NULL);
if (!dev)
{
mp_msg(MSGT_VO, MSGL_ERR, "zr: unable to find video device\n");
return 1;
}
}
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", dev);
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) {
/* 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;
}
zr->zrq.count = MJPEG_NBUFFERS;
zr->zrq.size = MJPEG_SIZE;
if (ioctl(zr->vdes, MJPIOC_REQBUFS, &zr->zrq)) {
mp_msg(MSGT_VO, MSGL_ERR, "zr: error requesting %d buffers of size %d\n", zr->zrq.count, zr->zrq.size);
return 1;
}
/* the buffer count allocated may be different to the request */
zr->buf = (unsigned char*)mmap(0, zr->zrq.count*zr->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", zr->zrq.count, zr->zrq.size);
return 1;
}
mp_msg(MSGT_VO, MSGL_V, "zr: got %d buffers of size %d (wanted %d buffers of size %d)\n", zr->zrq.count, zr->zrq.size, MJPEG_NBUFFERS, MJPEG_SIZE);
if (zr->zrq.count < MJPEG_NBUFFERS) {
mp_msg(MSGT_VO, MSGL_V, "zr: got not enough buffers\n");
return 1;
}
zr->queue = 0;
zr->synco = 0;
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");
if (munmap(zr->buf,zr->zrq.count*zr->zrq.size))
mp_msg(MSGT_VO, MSGL_ERR, "zr: error unmapping buffer\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");
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;
}
}
/* 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 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];
/* using MJPEG_NBUFFERS here, using the real number of
* buffers may give sync issues (real number of buffers
* is always sufficient) */
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*zr->zrq.size+k);
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");
}
/* 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(m_option_t* 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, "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"
"\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(m_option_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, "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;
}