2 * playback on Zoran cards, based on vo_zr.c
4 * copyright (C) 2001-2005 Rik Snel
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <linux/types.h>
37 #include <linux/videodev.h>
39 #include "videodev_mjpeg.h"
40 #include "video_out.h"
41 #include "video_out_internal.h"
43 #include "subopt-helper.h"
44 #include "fastmemcpy.h"
46 static const vo_info_t info
= {
47 "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
49 "Rik Snel <rsnel@cube.dyndns.org>",
53 const LIBVO_EXTERN(zr2
)
59 /* information for (and about) the zoran card */
61 unsigned char *buf
; /* the JPEGs will be placed here */
62 struct mjpeg_requestbuffers zrq
; /* info about this buffer */
64 int vdes
; /* file descriptor of card */
65 int playing
; /* 0 or 1 */
66 int frame
, sync
, queue
; /* buffer management */
67 struct mjpeg_sync zs
; /* state information */
68 struct mjpeg_params zp
;
69 struct video_capability vc
; /* max resolution and so on */
72 static vo_zr2_priv_t priv
;
74 #define ZR2_MJPEG_NBUFFERS 2
75 #define ZR2_MJPEG_SIZE 1024*256
77 /* some convenient #define's, is this portable enough? */
78 #define DBG2(...) mp_msg(MSGT_VO, MSGL_DBG2, "vo_zr2: " __VA_ARGS__)
79 #define VERBOSE(...) mp_msg(MSGT_VO, MSGL_V, "vo_zr2: " __VA_ARGS__)
80 #define ERROR(...) mp_msg(MSGT_VO, MSGL_ERR, "vo_zr2: " __VA_ARGS__)
81 #define WARNING(...) mp_msg(MSGT_VO, MSGL_WARN, "vo_zr2: " __VA_ARGS__)
83 static void stop_playing(vo_zr2_priv_t
*p
) {
86 if (ioctl(p
->vdes
, MJPIOC_QBUF_PLAY
, &p
->frame
) < 0)
87 ERROR("error stopping playback\n");
95 static const char *guess_device(const char *suggestion
, int inform
) {
98 static const char * const devs
[] = {
106 const char * const *dev
= devs
;
110 ERROR("error: specified device name is empty string\n");
114 res
= stat(suggestion
, &vstat
);
115 if (res
== 0 && S_ISCHR(vstat
.st_mode
)) {
116 if (inform
) VERBOSE("using device %s\n", suggestion
);
119 if (res
!= 0) ERROR("%s does not exist\n", suggestion
);
120 else ERROR("%s is no character device\n", suggestion
);
121 /* don't try to be smarter than the user, just exit */
126 while (*(++dev
) != NULL
) {
127 if (stat(*dev
, &vstat
) == 0 && S_ISCHR(vstat
.st_mode
)) {
128 VERBOSE("guessed video device %s\n", *dev
);
134 ERROR("unable to find video device\n");
139 static int query_format(uint32_t format
) {
140 if (format
==IMGFMT_ZRMJPEGNI
||
141 format
==IMGFMT_ZRMJPEGIT
||
142 format
==IMGFMT_ZRMJPEGIB
)
143 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
;
147 static uint32_t draw_image(mp_image_t
*mpi
) {
148 vo_zr2_priv_t
*p
= &priv
;
149 int size
= (int)mpi
->planes
[1];
150 if (size
> (int)p
->zrq
.size
) {
151 ERROR("incoming JPEG image (size=%d) doesn't fit in buffer\n",
156 /* looking for free buffer */
157 if (p
->queue
- p
->sync
< (int)p
->zrq
.count
) p
->frame
= p
->queue
;
159 if (ioctl(p
->vdes
, MJPIOC_SYNC
, &p
->zs
) < 0) {
160 ERROR("error waiting for buffer to become free\n");
163 p
->frame
= p
->zs
.frame
;
167 /* copy the jpeg image to the buffer which we acquired */
168 fast_memcpy(p
->buf
+ p
->zrq
.size
*p
->frame
, mpi
->planes
[0], size
);
173 static const char *normstring(int norm
) {
177 case VIDEO_MODE_NTSC
:
179 case VIDEO_MODE_SECAM
:
181 case VIDEO_MODE_AUTO
:
187 static int get_norm(const char *n
) {
188 if (!strcmp(n
, "PAL")) return VIDEO_MODE_PAL
;
189 if (!strcmp(n
, "NTSC")) return VIDEO_MODE_NTSC
;
190 if (!strcmp(n
, "SECAM")) return VIDEO_MODE_SECAM
;
191 if (!strcmp(n
, "auto")) return VIDEO_MODE_AUTO
;
192 return -1; /* invalid */
195 static int nc(void *normp
) {
196 const char **norm
= normp
;
197 if (get_norm(*norm
) == -1) {
198 ERROR("norm \"%s\" is not supported, choose from PAL, NTSC, SECAM and auto\n", *norm
);
203 static int pbc(void *prebufp
) {
204 int *prebuf
= prebufp
;
205 if (*prebuf
) WARNING("prebuffering is not yet supported\n");
209 static int preinit(const char *arg
) {
210 vo_zr2_priv_t
*p
= &priv
;
211 const char *dev
= NULL
;
212 char *dev_arg
= NULL
, *norm_arg
= NULL
;
213 int norm
= VIDEO_MODE_AUTO
, prebuf
= 0;
214 const opt_t subopts
[] = { /* don't want warnings with -Wall... */
215 { "dev", OPT_ARG_MSTRZ
, &dev_arg
, NULL
},
216 { "prebuf", OPT_ARG_BOOL
, &prebuf
, pbc
},
217 { "norm", OPT_ARG_MSTRZ
, &norm_arg
, nc
},
218 { NULL
, 0, NULL
, NULL
}
221 VERBOSE("preinit() called with arg: %s\n", arg
);
222 memset(p
, 0, sizeof(*p
)); /* set defaults */
225 if (subopt_parse(arg
, subopts
)) {
226 mp_msg(MSGT_VO
, MSGL_FATAL
,
227 "Allowed suboptions for -vo zr2 are:\n"
228 "- dev=DEVICE (default: %s)\n"
229 "- norm=PAL|NTSC|SECAM|auto (default: auto)\n"
230 "- prebuf/noprebuf (default:"
233 "Example: mplayer -vo zr2:dev=/dev/video1:"
234 "norm=PAL movie.avi\n\n"
235 , guess_device(NULL
, 0));
241 /* interpret the strings we got from subopt_parse */
243 norm
= get_norm(norm_arg
);
247 if (dev_arg
) dev
= dev_arg
;
249 dev
= guess_device(dev
, 1);
256 p
->vdes
= open(dev
, O_RDWR
);
258 ERROR("error opening %s: %s\n", dev
, strerror(errno
));
266 /* check if we really are dealing with a zoran card */
267 if (ioctl(p
->vdes
, MJPIOC_G_PARAMS
, &p
->zp
) < 0) {
268 ERROR("%s probably is not a DC10(+)/buz/lml33\n", dev
);
273 VERBOSE("kernel driver version %d.%d, current norm is %s\n",
274 p
->zp
.major_version
, p
->zp
.minor_version
,
275 normstring(p
->zp
.norm
));
277 /* changing the norm in the zoran_params and MJPIOC_S_PARAMS
278 * does nothing the last time I tried, so bail out if the norm
280 if (norm
!= VIDEO_MODE_AUTO
&& p
->zp
.norm
!= norm
) {
281 ERROR("mplayer currently can't change the video norm, "
282 "change it with (eg.) XawTV and retry.\n");
287 /* gather useful information */
288 if (ioctl(p
->vdes
, VIDIOCGCAP
, &p
->vc
) < 0) {
289 ERROR("error getting video capabilities from %s\n", dev
);
294 VERBOSE("card reports maxwidth=%d, maxheight=%d\n",
295 p
->vc
.maxwidth
, p
->vc
.maxheight
);
297 /* according to the mjpegtools source, some cards return a bogus
298 * vc.maxwidth, correct it here. If a new zoran card appears with a
299 * maxwidth different 640, 720 or 768 this code may lead to problems */
300 if (p
->vc
.maxwidth
!= 640 && p
->vc
.maxwidth
!= 768) {
301 VERBOSE("card probably reported bogus width (%d), "
302 "changing to 720\n", p
->vc
.maxwidth
);
303 p
->vc
.maxwidth
= 720;
306 p
->zrq
.count
= ZR2_MJPEG_NBUFFERS
;
307 p
->zrq
.size
= ZR2_MJPEG_SIZE
;
309 if (ioctl(p
->vdes
, MJPIOC_REQBUFS
, &p
->zrq
)) {
310 ERROR("error requesting %d buffers of size %d\n",
311 ZR2_MJPEG_NBUFFERS
, ZR2_MJPEG_NBUFFERS
);
316 VERBOSE("got %ld buffers of size %ld (wanted %d buffers of size %d)\n",
317 p
->zrq
.count
, p
->zrq
.size
, ZR2_MJPEG_NBUFFERS
,
320 p
->buf
= (unsigned char*)mmap(0, p
->zrq
.count
*p
->zrq
.size
,
321 PROT_READ
|PROT_WRITE
, MAP_SHARED
, p
->vdes
, 0);
323 if (p
->buf
== MAP_FAILED
) {
324 ERROR("error mapping requested buffers: %s", strerror(errno
));
332 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
333 uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
) {
334 int fields
= 1, top_first
= 1, err
= 0;
335 int stretchx
= 1, stretchy
= 1;
336 struct mjpeg_params zptmp
;
337 vo_zr2_priv_t
*p
= &priv
;
338 VERBOSE("config() called\n");
341 if (!query_format(format
)) {
342 ERROR("called with wrong format, should be impossible\n");
346 if ((int)height
> p
->vc
.maxheight
) {
347 ERROR("input height %d is too large, maxheight=%d\n",
348 height
, p
->vc
.maxheight
);
352 if (format
!= IMGFMT_ZRMJPEGNI
) {
354 if (format
== IMGFMT_ZRMJPEGIB
)
356 } else if ((int)height
> p
->vc
.maxheight
/2) {
357 ERROR("input is too high (%d) for non-interlaced playback"
358 "max=%d\n", height
, p
->vc
.maxheight
);
363 ERROR("input width=%d, must be multiple of 16\n", width
);
367 if (height
%(fields
*8) != 0) {
368 ERROR("input height=%d, must be multiple of %d\n",
373 /* we assume sample_aspect = 1 */
375 if (2*d_width
<= (uint32_t)p
->vc
.maxwidth
) {
376 VERBOSE("stretching x direction to preserve aspect\n");
378 } else VERBOSE("unable to preserve aspect, screen width "
382 if (d_width
== width
) stretchx
= 1;
383 else if (d_width
== 2*width
) stretchx
= 2;
384 #if 0 /* do minimal stretching for now */
385 else if (d_width
== 4*width
) stretchx
= 4;
386 else WARNING("d_width must be {1,2,4}*width, using defaults\n");
388 if (d_height
== height
) stretchy
= 1;
389 else if (d_height
== 2*height
) stretchy
= 2;
390 else if (d_height
== 4*height
) stretchy
= 4;
391 else WARNING("d_height must be {1,2,4}*height, using defaults\n");
394 if (stretchx
*width
> (uint32_t)p
->vc
.maxwidth
) {
395 ERROR("movie to be played is too wide, width=%d>maxwidth=%d\n",
396 width
*stretchx
, p
->vc
.maxwidth
);
400 if (stretchy
*height
> (uint32_t)p
->vc
.maxheight
) {
401 ERROR("movie to be played is too heigh, height=%d>maxheight"
402 "=%d\n", height
*stretchy
, p
->vc
.maxheight
);
406 if (err
== 1) return 1;
408 /* some video files (eg. concatenated MPEG files), make MPlayer
409 * call config() during playback while no parameters have changed.
410 * We make configuration changes to a temporary params structure,
411 * compare it with the old params structure and only apply the new
412 * config if it is different from the old one. */
413 memcpy(&zptmp
, &p
->zp
, sizeof(zptmp
));
415 /* translate the configuration to zoran understandable format */
416 zptmp
.decimation
= 0;
417 zptmp
.HorDcm
= stretchx
;
418 zptmp
.VerDcm
= stretchy
;
420 zptmp
.field_per_buff
= fields
;
421 zptmp
.odd_even
= top_first
;
423 /* center the image on screen */
424 zptmp
.img_x
= (p
->vc
.maxwidth
- width
*stretchx
)/2;
425 zptmp
.img_y
= (p
->vc
.maxheight
- height
*stretchy
*(3-fields
))/4;
427 zptmp
.img_width
= stretchx
*width
;
428 zptmp
.img_height
= stretchy
*height
/fields
;
430 VERBOSE("tv: %dx%d, out: %dx%d+%d+%d, in: %ux%u %s%s%s\n",
431 p
->vc
.maxwidth
, p
->vc
.maxheight
,
432 zptmp
.img_width
, 2*zptmp
.img_height
,
433 zptmp
.img_x
, 2*zptmp
.img_y
,
434 width
, height
, (fields
== 1) ? "non-interlaced" : "",
435 (fields
== 2 && top_first
== 1)
436 ? "interlaced top first" : "",
437 (fields
== 2 && top_first
== 0)
438 ? "interlaced bottom first" : "");
440 if (memcmp(&zptmp
, &p
->zp
, sizeof(zptmp
))) {
441 /* config differs, we must update */
442 memcpy(&p
->zp
, &zptmp
, sizeof(zptmp
));
444 if (ioctl(p
->vdes
, MJPIOC_S_PARAMS
, &p
->zp
) < 0) {
445 ERROR("error writing display params to card\n");
448 VERBOSE("successfully written display parameters to card\n");
449 } else VERBOSE("config didn't change, no need to write it to card\n");
454 static int control(uint32_t request
, void *data
) {
456 case VOCTRL_QUERY_FORMAT
:
457 return query_format(*((uint32_t*)data
));
458 case VOCTRL_DRAW_IMAGE
:
459 return draw_image(data
);
464 static int draw_frame(uint8_t *src
[]) {
468 static int draw_slice(uint8_t *image
[], int stride
[],
469 int w
, int h
, int x
, int y
) {
473 static void draw_osd(void) {
476 static void flip_page(void) {
477 vo_zr2_priv_t
*p
= &priv
;
478 /* queueing the buffer for playback */
479 /* queueing the first buffer automatically starts playback */
480 if (p
->playing
== 0) p
->playing
= 1;
481 if (ioctl(p
->vdes
, MJPIOC_QBUF_PLAY
, &p
->frame
) < 0)
482 ERROR("error queueing buffer for playback\n");
486 static void check_events(void) {
489 static void uninit(void) {
490 vo_zr2_priv_t
*p
= &priv
;
491 VERBOSE("uninit() called (may be called from preinit() on error)\n");
495 if (p
->buf
&& munmap(p
->buf
, p
->zrq
.size
*p
->zrq
.count
))
496 ERROR("error munmapping buffer: %s\n", strerror(errno
));
498 if (p
->vdes
>= 0) close(p
->vdes
);