2 * vo_zr2.c - playback on zoran cards
3 * Based on vo_zr.c,v 1.27
4 * Copyright (C) Rik Snel 2001-2005, License GNU GPL v2 or later
16 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <linux/types.h>
21 #include <linux/videodev.h>
23 #include "videodev_mjpeg.h"
24 #include "video_out.h"
25 #include "video_out_internal.h"
27 #include "subopt-helper.h"
28 #include "fastmemcpy.h"
30 static const vo_info_t info
= {
31 "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
33 "Rik Snel <rsnel@cube.dyndns.org>",
37 const LIBVO_EXTERN(zr2
)
43 /* information for (and about) the zoran card */
45 unsigned char *buf
; /* the JPEGs will be placed here */
46 struct mjpeg_requestbuffers zrq
; /* info about this buffer */
48 int vdes
; /* file descriptor of card */
49 int playing
; /* 0 or 1 */
50 int frame
, sync
, queue
; /* buffer management */
51 struct mjpeg_sync zs
; /* state information */
52 struct mjpeg_params zp
;
53 struct video_capability vc
; /* max resolution and so on */
56 static vo_zr2_priv_t priv
;
58 #define ZR2_MJPEG_NBUFFERS 2
59 #define ZR2_MJPEG_SIZE 1024*256
61 /* some convenient #define's, is this portable enough? */
62 #define DBG2(...) mp_msg(MSGT_VO, MSGL_DBG2, "vo_zr2: " __VA_ARGS__)
63 #define VERBOSE(...) mp_msg(MSGT_VO, MSGL_V, "vo_zr2: " __VA_ARGS__)
64 #define ERROR(...) mp_msg(MSGT_VO, MSGL_ERR, "vo_zr2: " __VA_ARGS__)
65 #define WARNING(...) mp_msg(MSGT_VO, MSGL_WARN, "vo_zr2: " __VA_ARGS__)
67 static void stop_playing(vo_zr2_priv_t
*p
) {
70 if (ioctl(p
->vdes
, MJPIOC_QBUF_PLAY
, &p
->frame
) < 0)
71 ERROR("error stopping playback\n");
79 static const char *guess_device(const char *suggestion
, int inform
) {
82 static const char * const devs
[] = {
90 const char * const *dev
= devs
;
94 ERROR("error: specified device name is empty string\n");
98 res
= stat(suggestion
, &vstat
);
99 if (res
== 0 && S_ISCHR(vstat
.st_mode
)) {
100 if (inform
) VERBOSE("using device %s\n", suggestion
);
103 if (res
!= 0) ERROR("%s does not exist\n", suggestion
);
104 else ERROR("%s is no character device\n", suggestion
);
105 /* don't try to be smarter than the user, just exit */
110 while (*(++dev
) != NULL
) {
111 if (stat(*dev
, &vstat
) == 0 && S_ISCHR(vstat
.st_mode
)) {
112 VERBOSE("guessed video device %s\n", *dev
);
118 ERROR("unable to find video device\n");
123 static int query_format(uint32_t format
) {
124 if (format
==IMGFMT_ZRMJPEGNI
||
125 format
==IMGFMT_ZRMJPEGIT
||
126 format
==IMGFMT_ZRMJPEGIB
)
127 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
131 static uint32_t draw_image(mp_image_t
*mpi
) {
132 vo_zr2_priv_t
*p
= &priv
;
133 int size
= (int)mpi
->planes
[1];
134 if (size
> (int)p
->zrq
.size
) {
135 ERROR("incoming JPEG image (size=%d) doesn't fit in buffer\n",
140 /* looking for free buffer */
141 if (p
->queue
- p
->sync
< (int)p
->zrq
.count
) p
->frame
= p
->queue
;
143 if (ioctl(p
->vdes
, MJPIOC_SYNC
, &p
->zs
) < 0) {
144 ERROR("error waiting for buffer to become free\n");
147 p
->frame
= p
->zs
.frame
;
151 /* copy the jpeg image to the buffer which we acquired */
152 fast_memcpy(p
->buf
+ p
->zrq
.size
*p
->frame
, mpi
->planes
[0], size
);
157 static const char *normstring(int norm
) {
161 case VIDEO_MODE_NTSC
:
163 case VIDEO_MODE_SECAM
:
165 case VIDEO_MODE_AUTO
:
171 static int get_norm(const char *n
) {
172 if (!strcmp(n
, "PAL")) return VIDEO_MODE_PAL
;
173 if (!strcmp(n
, "NTSC")) return VIDEO_MODE_NTSC
;
174 if (!strcmp(n
, "SECAM")) return VIDEO_MODE_SECAM
;
175 if (!strcmp(n
, "auto")) return VIDEO_MODE_AUTO
;
176 return -1; /* invalid */
179 static int nc(const char **norm
) {
180 if (get_norm(*norm
) == -1) {
181 ERROR("norm \"%s\" is not supported, choose from PAL, NTSC, SECAM and auto\n", *norm
);
186 static int pbc(int *prebuf
) {
187 if (*prebuf
) WARNING("prebuffering is not yet supported\n");
191 static int preinit(const char *arg
) {
192 vo_zr2_priv_t
*p
= &priv
;
193 const char *dev
= NULL
;
194 char *dev_arg
= NULL
, *norm_arg
= NULL
;
195 int norm
= VIDEO_MODE_AUTO
, prebuf
= 0;
196 opt_t subopts
[] = { /* don't want warnings with -Wall... */
197 { "dev", OPT_ARG_MSTRZ
, &dev_arg
, NULL
, 0 },
198 { "prebuf", OPT_ARG_BOOL
, &prebuf
, (opt_test_f
)pbc
, 0 },
199 { "norm", OPT_ARG_MSTRZ
, &norm_arg
, (opt_test_f
)nc
, 0 },
200 { NULL
, 0, NULL
, NULL
, 0 }
203 VERBOSE("preinit() called with arg: %s\n", arg
);
204 memset(p
, 0, sizeof(*p
)); /* set defaults */
207 if (subopt_parse(arg
, subopts
)) {
208 mp_msg(MSGT_VO
, MSGL_FATAL
,
209 "Allowed suboptions for -vo zr2 are:\n"
210 "- dev=DEVICE (default: %s)\n"
211 "- norm=PAL|NTSC|SECAM|auto (default: auto)\n"
212 "- prebuf/noprebuf (default:"
215 "Example: mplayer -vo zr2:dev=/dev/video1:"
216 "norm=PAL movie.avi\n\n"
217 , guess_device(NULL
, 0));
223 /* interpret the strings we got from subopt_parse */
225 norm
= get_norm(norm_arg
);
229 if (dev_arg
) dev
= dev_arg
;
231 dev
= guess_device(dev
, 1);
238 p
->vdes
= open(dev
, O_RDWR
);
240 ERROR("error opening %s: %s\n", dev
, strerror(errno
));
248 /* check if we really are dealing with a zoran card */
249 if (ioctl(p
->vdes
, MJPIOC_G_PARAMS
, &p
->zp
) < 0) {
250 ERROR("%s probably is not a DC10(+)/buz/lml33\n", dev
);
255 VERBOSE("kernel driver version %d.%d, current norm is %s\n",
256 p
->zp
.major_version
, p
->zp
.minor_version
,
257 normstring(p
->zp
.norm
));
259 /* changing the norm in the zoran_params and MJPIOC_S_PARAMS
260 * does nothing the last time I tried, so bail out if the norm
262 if (norm
!= VIDEO_MODE_AUTO
&& p
->zp
.norm
!= norm
) {
263 ERROR("mplayer currently can't change the video norm, "
264 "change it with (eg.) XawTV and retry.\n");
269 /* gather useful information */
270 if (ioctl(p
->vdes
, VIDIOCGCAP
, &p
->vc
) < 0) {
271 ERROR("error getting video capabilities from %s\n", dev
);
276 VERBOSE("card reports maxwidth=%d, maxheight=%d\n",
277 p
->vc
.maxwidth
, p
->vc
.maxheight
);
279 /* according to the mjpegtools source, some cards return a bogus
280 * vc.maxwidth, correct it here. If a new zoran card appears with a
281 * maxwidth different 640, 720 or 768 this code may lead to problems */
282 if (p
->vc
.maxwidth
!= 640 && p
->vc
.maxwidth
!= 768) {
283 VERBOSE("card probably reported bogus width (%d), "
284 "changing to 720\n", p
->vc
.maxwidth
);
285 p
->vc
.maxwidth
= 720;
288 p
->zrq
.count
= ZR2_MJPEG_NBUFFERS
;
289 p
->zrq
.size
= ZR2_MJPEG_SIZE
;
291 if (ioctl(p
->vdes
, MJPIOC_REQBUFS
, &p
->zrq
)) {
292 ERROR("error requesting %d buffers of size %d\n",
293 ZR2_MJPEG_NBUFFERS
, ZR2_MJPEG_NBUFFERS
);
298 VERBOSE("got %ld buffers of size %ld (wanted %d buffers of size %d)\n",
299 p
->zrq
.count
, p
->zrq
.size
, ZR2_MJPEG_NBUFFERS
,
302 p
->buf
= (unsigned char*)mmap(0, p
->zrq
.count
*p
->zrq
.size
,
303 PROT_READ
|PROT_WRITE
, MAP_SHARED
, p
->vdes
, 0);
305 if (p
->buf
== MAP_FAILED
) {
306 ERROR("error mapping requested buffers: %s", strerror(errno
));
314 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
315 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
) {
316 int fields
= 1, top_first
= 1, err
= 0;
317 int stretchx
= 1, stretchy
= 1;
318 struct mjpeg_params zptmp
;
319 vo_zr2_priv_t
*p
= &priv
;
320 VERBOSE("config() called\n");
323 if (!query_format(format
)) {
324 ERROR("called with wrong format, should be impossible\n");
328 if ((int)height
> p
->vc
.maxheight
) {
329 ERROR("input height %d is too large, maxheight=%d\n",
330 height
, p
->vc
.maxheight
);
334 if (format
!= IMGFMT_ZRMJPEGNI
) {
336 if (format
== IMGFMT_ZRMJPEGIB
)
338 } else if ((int)height
> p
->vc
.maxheight
/2) {
339 ERROR("input is too high (%d) for non-interlaced playback"
340 "max=%d\n", height
, p
->vc
.maxheight
);
345 ERROR("input width=%d, must be multiple of 16\n", width
);
349 if (height
%(fields
*8) != 0) {
350 ERROR("input height=%d, must be multiple of %d\n",
355 /* we assume sample_aspect = 1 */
357 if (2*d_width
<= (uint32_t)p
->vc
.maxwidth
) {
358 VERBOSE("stretching x direction to preserve aspect\n");
360 } else VERBOSE("unable to preserve aspect, screen width "
364 if (d_width
== width
) stretchx
= 1;
365 else if (d_width
== 2*width
) stretchx
= 2;
366 #if 0 /* do minimal stretching for now */
367 else if (d_width
== 4*width
) stretchx
= 4;
368 else WARNING("d_width must be {1,2,4}*width, using defaults\n");
370 if (d_height
== height
) stretchy
= 1;
371 else if (d_height
== 2*height
) stretchy
= 2;
372 else if (d_height
== 4*height
) stretchy
= 4;
373 else WARNING("d_height must be {1,2,4}*height, using defaults\n");
376 if (stretchx
*width
> (uint32_t)p
->vc
.maxwidth
) {
377 ERROR("movie to be played is too wide, width=%d>maxwidth=%d\n",
378 width
*stretchx
, p
->vc
.maxwidth
);
382 if (stretchy
*height
> (uint32_t)p
->vc
.maxheight
) {
383 ERROR("movie to be played is too heigh, height=%d>maxheight"
384 "=%d\n", height
*stretchy
, p
->vc
.maxheight
);
388 if (err
== 1) return 1;
390 /* some video files (eg. concatenated MPEG files), make MPlayer
391 * call config() during playback while no parameters have changed.
392 * We make configuration changes to a temporary params structure,
393 * compare it with the old params structure and only apply the new
394 * config if it is different from the old one. */
395 memcpy(&zptmp
, &p
->zp
, sizeof(zptmp
));
397 /* translate the configuration to zoran understandable format */
398 zptmp
.decimation
= 0;
399 zptmp
.HorDcm
= stretchx
;
400 zptmp
.VerDcm
= stretchy
;
402 zptmp
.field_per_buff
= fields
;
403 zptmp
.odd_even
= top_first
;
405 /* center the image on screen */
406 zptmp
.img_x
= (p
->vc
.maxwidth
- width
*stretchx
)/2;
407 zptmp
.img_y
= (p
->vc
.maxheight
- height
*stretchy
*(3-fields
))/4;
409 zptmp
.img_width
= stretchx
*width
;
410 zptmp
.img_height
= stretchy
*height
/fields
;
412 VERBOSE("tv: %dx%d, out: %dx%d+%d+%d, in: %ux%u %s%s%s\n",
413 p
->vc
.maxwidth
, p
->vc
.maxheight
,
414 zptmp
.img_width
, 2*zptmp
.img_height
,
415 zptmp
.img_x
, 2*zptmp
.img_y
,
416 width
, height
, (fields
== 1) ? "non-interlaced" : "",
417 (fields
== 2 && top_first
== 1)
418 ? "interlaced top first" : "",
419 (fields
== 2 && top_first
== 0)
420 ? "interlaced bottom first" : "");
422 if (memcmp(&zptmp
, &p
->zp
, sizeof(zptmp
))) {
423 /* config differs, we must update */
424 memcpy(&p
->zp
, &zptmp
, sizeof(zptmp
));
426 if (ioctl(p
->vdes
, MJPIOC_S_PARAMS
, &p
->zp
) < 0) {
427 ERROR("error writing display params to card\n");
430 VERBOSE("successfully written display parameters to card\n");
431 } else VERBOSE("config didn't change, no need to write it to card\n");
436 static int control(uint32_t request
, void *data
) {
438 case VOCTRL_QUERY_FORMAT
:
439 return query_format(*((uint32_t*)data
));
440 case VOCTRL_DRAW_IMAGE
:
441 return draw_image(data
);
446 static int draw_frame(uint8_t *src
[]) {
450 static int draw_slice(uint8_t *image
[], int stride
[],
451 int w
, int h
, int x
, int y
) {
455 static void draw_osd(void) {
458 static void flip_page(void) {
459 vo_zr2_priv_t
*p
= &priv
;
460 /* queueing the buffer for playback */
461 /* queueing the first buffer automatically starts playback */
462 if (p
->playing
== 0) p
->playing
= 1;
463 if (ioctl(p
->vdes
, MJPIOC_QBUF_PLAY
, &p
->frame
) < 0)
464 ERROR("error queueing buffer for playback\n");
468 static void check_events(void) {
471 static void uninit(void) {
472 vo_zr2_priv_t
*p
= &priv
;
473 VERBOSE("uninit() called (may be called from preinit() on error)\n");
477 if (p
->buf
&& munmap(p
->buf
, p
->zrq
.size
*p
->zrq
.count
))
478 ERROR("error munmapping buffer: %s\n", strerror(errno
));
480 if (p
->vdes
>= 0) close(p
->vdes
);