2 * playback on Zoran cards
3 * copyright (C) 2001, 2003 Rik Snel
5 * This file is part of MPlayer.
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <linux/types.h>
36 #include <linux/videodev.h>
38 #include "videodev_mjpeg.h"
39 #include "video_out.h"
40 #include "video_out_internal.h"
43 #include "fastmemcpy.h"
47 static const vo_info_t info
=
49 "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
51 "Rik Snel <rsnel@cube.dyndns.org>",
55 const LIBVO_EXTERN (zr
)
57 #define ZR_MAX_DEVICES 4
58 /* General variables */
68 static int zr_count
= 1;
69 static int zr_parsing
= 0;
73 /* commandline args given for this device (and defaults) */
74 int vdec
, hdec
; /* requested decimation 1,2,4 */
75 int fd
; /* force decimation */
76 int xdoff
, ydoff
; /* offset from upperleft of screen
77 * default is 'centered' */
78 int quality
; /* jpeg quality 1=best, 20=bad */
79 geo_t g
; /* view window (zrcrop) */
80 char *device
; /* /dev/video1 */
81 int bw
; /* if bw == 1, display in black&white */
82 int norm
; /* PAL/NTSC */
84 /* buffers + pointers + info */
87 int image_width
, image_height
, size
;
88 int off_y
, off_c
, stride
; /* for use by 'draw slice/frame' */
90 unsigned char *buf
; /* the jpeg images will be placed here */
92 unsigned char *y_data
, *u_data
, *v_data
; /* used by the jpeg encoder */
93 int y_stride
, u_stride
, v_stride
; /* these point somewhere in image */
95 /* information for (and about) the zoran card */
97 int vdes
; /* file descriptor of card */
98 int frame
, synco
, queue
; /* buffer management */
99 struct mjpeg_sync zs
; /* state information */
100 struct mjpeg_params p
;
101 struct mjpeg_requestbuffers zrq
;
102 struct video_capability vc
; /* max resolution and so on */
103 int fields
, stretchy
; /* must the *image be interlaced
104 or stretched to fit on the screen? */
107 static zr_info_t zr_info
[ZR_MAX_DEVICES
] = {
108 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL
, 0, VIDEO_MODE_AUTO
, NULL
, 0, 0, 0, 0, 0,
109 0, NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
110 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL
, 0, VIDEO_MODE_AUTO
, NULL
, 0, 0, 0, 0, 0,
111 0, NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
112 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL
, 0, VIDEO_MODE_AUTO
, NULL
, 0, 0, 0, 0, 0,
113 0, NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
114 {1, 1, 1, -1, -1, 2, {0, 0, 0, 0, 0}, NULL
, 0, VIDEO_MODE_AUTO
, NULL
, 0, 0, 0, 0, 0,
115 0, NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
120 #define MJPEG_NBUFFERS 2
121 #define MJPEG_SIZE 1024*256
124 int zoran_getcap(zr_info_t
*zr
) {
131 const char *devs
[] = {
143 if ((stat(devs
[i
], &vstat
) == 0) && S_ISCHR(vstat
.st_mode
))
146 mp_msg(MSGT_VO
, MSGL_V
, "zr: found video device %s\n", dev
);
149 } while (devs
[++i
] != NULL
);
153 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: unable to find video device\n");
158 zr
->vdes
= open(dev
, O_RDWR
);
161 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error opening %s: %s\n",
162 dev
, strerror(errno
));
166 /* before we can ask for the maximum resolution, we must set
167 * the correct tv norm */
169 if (ioctl(zr
->vdes
, MJPIOC_G_PARAMS
, &zr
->p
) < 0) {
170 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: device at %s is probably not a DC10(+)/buz/lml33\n", dev
);
174 if (zr
->p
.norm
!= zr
->norm
&& zr
->norm
!= VIDEO_MODE_AUTO
) {
175 /* attempt to set requested norm */
176 zr
->p
.norm
= zr
->norm
;
177 if (ioctl(zr
->vdes
, MJPIOC_S_PARAMS
, &zr
->p
) < 0) {
178 mp_msg(MSGT_VO
, MSGL_ERR
,
179 "zr: unable to change video norm, use another program to change it (XawTV)\n");
182 ioctl(zr
->vdes
, MJPIOC_G_PARAMS
, &zr
->p
);
183 if (zr
->norm
!= zr
->p
.norm
) {
184 mp_msg(MSGT_VO
, MSGL_ERR
,
185 "zr: unable to change video norm, use another program to change it (XawTV)\n");
190 if (ioctl(zr
->vdes
, VIDIOCGCAP
, &zr
->vc
) < 0) {
191 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error getting video capabilities from %s\n", dev
);
194 mp_msg(MSGT_VO
, MSGL_V
, "zr: MJPEG card reports maxwidth=%d, maxheight=%d\n", zr
->vc
.maxwidth
, zr
->vc
.maxheight
);
199 int init_zoran(zr_info_t
*zr
, int stretchx
, int stretchy
) {
200 /* center the image, and stretch it as far as possible (try to keep
201 * aspect) and check if it fits */
202 if (zr
->image_width
> zr
->vc
.maxwidth
) {
203 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: movie to be played is too wide, max width currently %d\n", zr
->vc
.maxwidth
);
207 if (zr
->image_height
> zr
->vc
.maxheight
) {
208 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: movie to be played is too high, max height currently %d\n", zr
->vc
.maxheight
);
212 zr
->p
.decimation
= 0;
213 zr
->p
.HorDcm
= stretchx
;
214 zr
->p
.VerDcm
= stretchy
;
216 zr
->p
.field_per_buff
= zr
->fields
;
217 if (zr
->xdoff
== -1) {
218 zr
->p
.img_x
= (zr
->vc
.maxwidth
-
219 zr
->p
.HorDcm
*(int)zr
->image_width
/zr
->hdec
)/2;
221 zr
->p
.img_x
= zr
->xdoff
;
223 if (zr
->ydoff
== -1) {
224 zr
->p
.img_y
= (zr
->vc
.maxheight
- zr
->p
.VerDcm
*
225 (3-zr
->fields
)*(int)zr
->image_height
)/4;
227 zr
->p
.img_y
= zr
->ydoff
;
229 zr
->p
.img_width
= zr
->p
.HorDcm
*zr
->image_width
/zr
->hdec
;
230 zr
->p
.img_height
= zr
->p
.VerDcm
*zr
->image_height
/zr
->fields
;
231 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
);
233 if (ioctl(zr
->vdes
, MJPIOC_S_PARAMS
, &zr
->p
) < 0) {
234 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error setting display parameters\n");
238 zr
->zrq
.count
= MJPEG_NBUFFERS
;
239 zr
->zrq
.size
= MJPEG_SIZE
;
241 if (ioctl(zr
->vdes
, MJPIOC_REQBUFS
, &zr
->zrq
)) {
242 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error requesting %ld buffers of size %ld\n", zr
->zrq
.count
, zr
->zrq
.size
);
246 /* the buffer count allocated may be different to the request */
247 zr
->buf
= (unsigned char*)mmap(0, zr
->zrq
.count
*zr
->zrq
.size
,
248 PROT_READ
|PROT_WRITE
, MAP_SHARED
, zr
->vdes
, 0);
250 if (zr
->buf
== MAP_FAILED
) {
251 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error requesting %ld buffers of size %ld\n", zr
->zrq
.count
, zr
->zrq
.size
);
255 mp_msg(MSGT_VO
, MSGL_V
, "zr: got %ld buffers of size %ld (wanted %d buffers of size %d)\n", zr
->zrq
.count
, zr
->zrq
.size
, MJPEG_NBUFFERS
, MJPEG_SIZE
);
256 if (zr
->zrq
.count
< MJPEG_NBUFFERS
) {
257 mp_msg(MSGT_VO
, MSGL_V
, "zr: got not enough buffers\n");
267 void uninit_zoran(zr_info_t
*zr
) {
272 while (zr
->queue
> zr
->synco
+ 1) {
273 if (ioctl(zr
->vdes
, MJPIOC_SYNC
, &zr
->zs
) < 0)
274 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error waiting for buffers to become free\n");
279 if (ioctl(zr
->vdes
, MJPIOC_QBUF_PLAY
, &zr
->frame
) < 0)
280 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error stopping playback of last frame\n");
281 if (munmap(zr
->buf
,zr
->zrq
.count
*zr
->zrq
.size
))
282 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error unmapping buffer\n");
286 int zr_geometry_sane(geo_t
*g
, unsigned int width
, unsigned int height
) {
288 if (g
->width
%2 != 0 || g
->height
%2 != 0 ||
289 g
->xoff
%2 != 0 || g
->yoff
%2 != 0) {
290 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: arguments in -zrcrop must be multiples of 2\n");
293 if (g
->width
<= 0 || g
->height
<= 0 ||
294 g
->xoff
< 0 || g
->yoff
< 0) {
295 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: width and height must be positive and offset nonnegative\n");
298 if (g
->width
+ g
->xoff
> width
) {
299 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: width+xoffset (%d+%d>%d) is too big\n", g
->width
, g
->xoff
, width
);
302 if (g
->height
+ g
->yoff
> height
) {
303 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: height+yoffset (%d+%d>%d) is too big\n", g
->height
, g
->yoff
, height
);
317 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
318 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
320 int i
, tmp
, stretchx
, stretchy
;
322 if (format
!= IMGFMT_YV12
&& format
!= IMGFMT_YUY2
) {
323 printf("vo_zr called with wrong format");
326 for (i
= 0; i
< zr_count
; i
++) {
327 zr_info_t
*zr
= &zr_info
[i
];
330 zr
->stride
= 2*width
;
331 if (zr_geometry_sane(g
, width
, height
)) return 1;
333 /* we must know the maximum resolution of the device
334 * it differs for DC10+ and buz for example */
335 zoran_getcap(zr
); /*must be called before init_zoran */
336 /* make the scaling decision
337 * we are capable of stretching the image in the horizontal
338 * direction by factors 1, 2 and 4
339 * we can stretch the image in the vertical direction by a
340 * factor of 1 and 2 AND we must decide about interlacing */
341 if (g
->width
> zr
->vc
.maxwidth
/2 ||
342 g
->height
> zr
->vc
.maxheight
/2) {
348 } else if (zr
->vdec
== 4) {
353 } else if (g
->width
> zr
->vc
.maxwidth
/4 ||
354 g
->height
> zr
->vc
.maxheight
/4) {
360 } else if (zr
->vdec
== 4) {
362 mp_msg(MSGT_VO
, MSGL_WARN
, "zr: vertical decimation too high, changing to 2 (use -zrfd to keep vdec=4)\n");
369 } else if (zr
->hdec
== 4){
371 mp_msg(MSGT_VO
, MSGL_WARN
, "zr: horizontal decimation too high, changing to 2 (use -zrfd to keep hdec=4)\n");
377 /* output image is maximally stretched */
381 if (zr
->vdec
!= 1 && !zr
->fd
) {
382 mp_msg(MSGT_VO
, MSGL_WARN
, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep vdec=%d)\n", zr
->vdec
);
385 if (zr
->hdec
!= 1 && !zr
->fd
) {
386 mp_msg(MSGT_VO
, MSGL_WARN
, "zr: vertical decimation too high, changing to 1 (use -zrfd to keep hdec=%d)\n", zr
->hdec
);
390 /* It can be that the original frame was too big for display,
391 * or that the width of the decimated image (for example) after
392 * padding up to a multiple of 16 has become too big. (orig
393 * width 720 (exactly right for the Buz) after decimation 360,
394 * after padding up to a multiple of 16 368, display 736 -> too
395 * large). In these situations we auto(re)crop. */
396 tmp
= 16*((g
->width
- 1)/(zr
->hdec
*16) + 1);
397 if (stretchx
*tmp
> zr
->vc
.maxwidth
) {
398 g
->xoff
+= 2*((g
->width
- zr
->hdec
*(tmp
-16))/4);
399 /* g->off must be a multiple of 2 */
400 g
->width
= zr
->hdec
*(tmp
- 16);
401 g
->set
= 0; /* we abuse this field to
402 report that g has changed*/
404 tmp
= 8*zr
->fields
*((g
->height
- 1)/(zr
->vdec
*zr
->fields
*8)+1);
405 if (stretchy
*tmp
> zr
->vc
.maxheight
) {
406 g
->yoff
+= 2*((g
->height
- zr
->vdec
*
407 (tmp
- 8*zr
->fields
))/4);
408 g
->height
= zr
->vdec
*(tmp
- 8*zr
->fields
);
412 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
);
414 /* the height must be a multiple of fields*8 and the width
415 * must be a multiple of 16 */
416 /* add some black borders to make it so, and center the image*/
417 zr
->image_height
= zr
->fields
*8*((g
->height
/zr
->vdec
- 1)/
419 zr
->image_width
= (zr
->hdec
*16)*((g
->width
- 1)/(zr
->hdec
*16) + 1);
420 zr
->off_y
= (zr
->image_height
- g
->height
/zr
->vdec
)/2;
421 if (zr
->off_y
%2 != 0) zr
->off_y
++;
422 zr
->off_y
*= zr
->image_width
;
423 zr
->off_c
= zr
->off_y
/4;
424 zr
->off_y
+= (zr
->image_width
- g
->width
)/2;
425 if (zr
->off_y
%2 != 0) zr
->off_y
--;
426 zr
->off_c
+= (zr
->image_width
- g
->width
)/4;
427 zr
->size
= zr
->image_width
*zr
->image_height
;
428 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
);
430 zr
->image
= malloc(2*zr
->size
); /* this buffer allows for YUV422 data,
431 * so it is a bit too big for YUV420 */
433 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: Memory exhausted\n");
436 /* and make sure that the borders are _really_ black */
439 memset(zr
->image
, 0, zr
->size
);
440 memset(zr
->image
+ zr
->size
, 0x80, zr
->size
/4);
441 memset(zr
->image
+ 3*zr
->size
/2, 0x80, zr
->size
/4);
442 zr
->y_data
= zr
->image
;
443 zr
->u_data
= zr
->image
+ zr
->size
;
444 zr
->v_data
= zr
->image
+ 3*zr
->size
/2;
446 zr
->y_stride
= zr
->image_width
;
447 zr
->u_stride
= zr
->image_width
/2;
448 zr
->v_stride
= zr
->image_width
/2;
450 zr
->j
= jpeg_enc_init(zr
->image_width
/zr
->hdec
,
451 zr
->image_height
/zr
->fields
,
452 zr
->hdec
, zr
->y_stride
*zr
->fields
,
453 zr
->hdec
, zr
->u_stride
*zr
->fields
,
454 zr
->hdec
, zr
->v_stride
*zr
->fields
,
455 1, zr
->quality
, zr
->bw
);
458 for (tmp
= 0; tmp
< 2*zr
->size
; tmp
+=4) {
460 zr
->image
[tmp
+1] = 0x80;
461 zr
->image
[tmp
+2] = 0;
462 zr
->image
[tmp
+3] = 0x80;
465 zr
->y_data
= zr
->image
;
466 zr
->u_data
= zr
->image
+ 1;
467 zr
->v_data
= zr
->image
+ 3;
469 zr
->y_stride
= 2*zr
->image_width
;
470 zr
->u_stride
= 2*zr
->image_width
;
471 zr
->v_stride
= 2*zr
->image_width
;
473 zr
->j
= jpeg_enc_init(zr
->image_width
/zr
->hdec
,
474 zr
->image_height
/zr
->fields
,
476 zr
->y_stride
*zr
->fields
,
478 zr
->u_stride
*zr
->fields
,
480 zr
->v_stride
*zr
->fields
,
481 0, zr
->quality
, zr
->bw
);
484 mp_msg(MSGT_VO
, MSGL_FATAL
, "zr: internal inconsistency in vo_zr\n");
489 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error initializing the jpeg encoder\n");
493 if (init_zoran(zr
, stretchx
, stretchy
)) {
501 static void draw_osd(void) {
504 static void flip_page (void) {
507 //char filename[100];
508 /* do we have a free buffer? */
509 for (j
= 0; j
< zr_count
; j
++) {
510 zr_info_t
*zr
= &zr_info
[j
];
511 /* using MJPEG_NBUFFERS here, using the real number of
512 * buffers may give sync issues (real number of buffers
513 * is always sufficient) */
514 if (zr
->queue
-zr
->synco
< MJPEG_NBUFFERS
) {
515 zr
->frame
= zr
->queue
;
517 if (ioctl(zr
->vdes
, MJPIOC_SYNC
, &zr
->zs
) < 0)
518 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error waiting for buffers to become free\n");
519 zr
->frame
= zr
->zs
.frame
;
523 for (i
= 0; i
< zr
->fields
; i
++)
524 k
+=jpeg_enc_frame(zr
->j
, zr
->y_data
+ i
*zr
->y_stride
,
525 zr
->u_data
+ i
*zr
->u_stride
,
526 zr
->v_data
+ i
*zr
->v_stride
,
527 zr
->buf
+ zr
->frame
*zr
->zrq
.size
+k
);
528 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");
530 /* Warning: Only the first jpeg image contains huffman- and
531 * quantisation tables, so don't expect files other than
532 * test0001.jpg to be readable */
533 /*sprintf(filename, "test%04d.jpg", framenum);
534 fp = fopen(filename, "w");
536 fwrite(buf+frame*zrq.size, 1, k, fp);
538 /*fp = fopen("test1.jpg", "r");
539 fread(buf+frame*zrq.size, 1, 2126, fp);
542 for (j
= 0; j
< zr_count
; j
++) {
543 zr_info_t
*zr
= &zr_info
[j
];
544 if (ioctl(zr
->vdes
, MJPIOC_QBUF_PLAY
, &zr
->frame
) < 0)
545 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: error queueing buffer for playback\n");
553 static int draw_frame(uint8_t * src
[]) {
556 //printf("draw frame called\n");
557 for (j
= 0; j
< zr_count
; j
++) {
558 zr_info_t
*zr
= &zr_info
[j
];
560 source
= src
[0] + 2*g
->yoff
*zr
->vdec
*zr
->stride
+ 2*g
->xoff
;
561 dest
= zr
->image
+ 2*zr
->off_y
;
562 for (i
= 0; i
< g
->height
/zr
->vdec
; i
++) {
563 fast_memcpy(dest
, source
, zr
->image_width
*2);
564 dest
+= 2*zr
->image_width
;
565 source
+= zr
->vdec
*zr
->stride
;
571 static int query_format(uint32_t format
) {
572 if(format
==IMGFMT_YV12
|| format
==IMGFMT_YUY2
)
573 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
577 static void uninit(void) {
579 mp_msg(MSGT_VO
, MSGL_V
, "zr: uninit called\n");
580 for (j
= 0; j
< zr_count
; j
++) {
581 jpeg_enc_uninit(zr_info
[j
].j
);
582 uninit_zoran(&zr_info
[j
]);
586 static void check_events(void) {
590 static int draw_slice(uint8_t *srcimg
[], int stride
[],
591 int wf
, int hf
, int xf
, int yf
) {
592 int i
, j
, w
, h
, x
, y
;
593 /* Apply 'geometry', crop unwanted parts */
595 //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]);
596 for (j
= 0; j
< zr_count
; j
++) {
597 uint8_t *src
=srcimg
[0];
598 uint8_t *src1
=srcimg
[1];
599 uint8_t *src2
=srcimg
[2];
600 zr_info_t
*zr
= &zr_info
[j
];
602 w
= wf
; h
= hf
; x
= xf
; y
= yf
;
605 src1
+= (g
->xoff
- x
)/2;
606 src2
+= (g
->xoff
- x
)/2;
608 if (w
< 0) break; //return 0;
613 if (x
+ w
> g
->width
) {
615 if (w
< 0) break; //return 0;
618 src
+= (g
->yoff
- y
)*stride
[0];
619 src1
+= ((g
->yoff
- y
)/2)*stride
[1];
620 src2
+= ((g
->yoff
- y
)/2)*stride
[2];
622 if (h
< 0) break; //return 0;
627 if (y
+ h
> g
->height
) {
629 if (h
< 0) break; //return 0;
631 //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]);
632 dst
=zr
->image
+ zr
->off_y
+ zr
->image_width
*(y
/zr
->vdec
)+x
;
634 for (i
= 0; i
< h
; i
++) {
635 if ((i
+ x
)%zr
->vdec
== 0) {
636 fast_memcpy(dst
,src
,w
);
637 dst
+=zr
->image_width
;
644 uint8_t *dst1
=zr
->image
+ zr
->size
+ zr
->off_c
+ (y
/(zr
->vdec
*2))*zr
->image_width
/2+(x
/2);
645 uint8_t *dst2
=zr
->image
+ 3*zr
->size
/2 + zr
->off_c
+
647 zr
->image_width
/2+(x
/2);
648 for (i
= 0; i
< h
/2; i
++) {
649 if ((i
+x
/2)%zr
->vdec
== 0) {
650 fast_memcpy(dst1
,src1
,w
/2);
651 fast_memcpy(dst2
,src2
,w
/2);
652 dst1
+=zr
->image_width
/2;
653 dst2
+=zr
->image_width
/2;
664 /* copied and adapted from vo_aa_parseoption */
666 vo_zr_parseoption(const m_option_t
* conf
, const char *opt
, const char *param
){
667 /* got an option starting with zr */
668 zr_info_t
*zr
= &zr_info
[zr_parsing
];
670 /* do WE need it ?, always */
671 if (!strcasecmp(opt
, "zrdev")) {
672 if (param
== NULL
) return ERR_MISSING_PARAM
;
673 //if ((i=getcolor(param))==-1) return ERR_OUT_OF_RANGE;
676 zr
->device
= malloc(strlen(param
)+1);
677 strcpy(zr
->device
, param
);
678 mp_msg(MSGT_VO
, MSGL_V
, "zr: using device %s\n", zr
->device
);
680 } else if (!strcasecmp(opt
, "zrbw")) {
682 return ERR_OUT_OF_RANGE
;
686 } else if (!strcasecmp(opt
, "zrfd")) {
688 return ERR_OUT_OF_RANGE
;
692 } else if (!strcasecmp(opt
, "zrcrop")){
697 zr
= &zr_info
[zr_parsing
];
700 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: too many simultaneus display devices requested (max. is 4)\n");
701 return ERR_OUT_OF_RANGE
;
704 if (param
== NULL
) return ERR_MISSING_PARAM
;
705 if (sscanf(param
, "%dx%d+%d+%d", &g
->width
, &g
->height
,
706 &g
->xoff
, &g
->yoff
) != 4) {
707 g
->xoff
= 0; g
->yoff
= 0;
708 if (sscanf(param
, "%dx%d", &g
->width
, &g
->height
) != 2) {
709 mp_msg(MSGT_VO
, MSGL_ERR
, "zr: argument to -zrcrop must be of the form 352x288+16+0\n");
710 return ERR_OUT_OF_RANGE
;
714 mp_msg(MSGT_VO
, MSGL_V
, "zr: cropping %s\n", param
);
716 }else if (!strcasecmp(opt
, "zrhdec")) {
718 if (i
!= 1 && i
!= 2 && i
!= 4) return ERR_OUT_OF_RANGE
;
721 }else if (!strcasecmp(opt
, "zrvdec")) {
723 if (i
!= 1 && i
!= 2 && i
!= 4) return ERR_OUT_OF_RANGE
;
726 }else if (!strcasecmp(opt
, "zrxdoff")) {
730 }else if (!strcasecmp(opt
, "zrydoff")) {
734 }else if (!strcasecmp(opt
, "zrquality")) {
736 if (i
< 1 || i
> 20) return ERR_OUT_OF_RANGE
;
739 }else if (!strcasecmp(opt
, "zrnorm")) {
740 if (param
== NULL
) return ERR_MISSING_PARAM
;
741 if (!strcasecmp(param
, "NTSC")) {
742 mp_msg(MSGT_VO
, MSGL_V
, "zr: Norm set to NTSC\n");
743 zr
->norm
= VIDEO_MODE_NTSC
;
745 } else if (!strcasecmp(param
, "PAL")) {
746 mp_msg(MSGT_VO
, MSGL_V
, "zr: Norm set to PAL\n");
747 zr
->norm
= VIDEO_MODE_PAL
;
750 return ERR_OUT_OF_RANGE
;
752 }else if (!strcasecmp(opt
, "zrhelp")){
753 printf("Help for -vo zr: Zoran ZR360[56]7/ZR36060 based MJPEG capture/playback cards\n");
755 printf("Here are the zr options:\n");
758 " -zrcrop specify part of the input image that\n"
759 " you want to see as an x-style geometry string\n"
760 " example: -zrcrop 352x288+16+0\n"
761 " -zrvdec vertical decimation 1, 2 or 4\n"
762 " -zrhdec horizontal decimation 1, 2 or 4\n"
763 " -zrfd decimation is only done if the primitive\n"
764 " hardware upscaler can correct for the decimation,\n"
765 " this switch allows you to see the effects\n"
766 " of too much decimation\n"
767 " -zrbw display in black&white (speed increase)\n"
768 " -zrxdoff x offset from upper-left of TV screen (default is 'centered')\n"
769 " -zrydoff y offset from upper-left of TV screen (default is 'centered')\n"
770 " -zrquality jpeg compression quality [BEST] 1 - 20 [VERY BAD]\n"
771 " -zrdev playback device (example -zrdev /dev/video1)\n"
772 " -zrnorm specify norm PAL/NTSC (default: leave at current setting)\n"
774 "Cinerama support: additional occurances of -zrcrop activate cinerama mode,\n"
775 "suppose you have a 704x272 movie, two DC10+ cards and two beamers (or tv's),\n"
776 "then you would issue the following command:\n\n"
777 "mplayer -vo zr -zrcrop 352x272+0+0 -zrdev /dev/video0 -zrcrop 352x272+352+0 \\\n"
778 " -zrdev /dev/video1 movie.avi\n\n"
779 "Options appearing after the second -zrcrop apply to the second card, it is\n"
780 "possible to dispay at a different jpeg quality or at different decimations.\n\n"
781 "The parameters -zrxdoff and -zrydoff can be used to align the two images.\n"
782 "The maximum number of zoran cards participating in cinerama is 4, so you can\n"
783 "build a 2x2 vidiwall. (untested for obvious reasons, the setup wit a buz and\n"
784 "a DC10+ (and no beamers) is tested, however)\n"
789 return ERR_NOT_AN_OPTION
;
792 void vo_zr_revertoption(const m_option_t
* opt
,const char* param
) {
794 zr_info_t
*zr
= &zr_info
[1];
798 if (!strcasecmp(param
, "zrdev")) {
802 } else if (!strcasecmp(param
, "zrbw"))
804 else if (!strcasecmp(param
, "zrfd"))
806 else if (!strcasecmp(param
, "zrcrop"))
807 zr
->g
.set
= zr
->g
.xoff
= zr
->g
.yoff
= 0;
808 else if (!strcasecmp(param
, "zrhdec"))
810 else if (!strcasecmp(param
, "zrvdec"))
812 else if (!strcasecmp(param
, "zrxdoff"))
814 else if (!strcasecmp(param
, "zrydoff"))
816 else if (!strcasecmp(param
, "zrquality"))
818 else if (!strcasecmp(param
, "zrnorm"))
819 zr
->norm
= VIDEO_MODE_AUTO
;
823 static int preinit(const char *arg
)
827 printf("vo_zr: Unknown subdevice: %s\n",arg
);
833 static int control(uint32_t request
, void *data
)
836 case VOCTRL_QUERY_FORMAT
:
837 return query_format(*((uint32_t*)data
));