Make some functions static.
[mplayer.git] / libvo / vo_zr2.c
blob760294d87f73c20b1a49b88090ec98337a4941fd
1 /*
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
5 */
7 /* $Id$ */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <errno.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include <sys/mman.h>
19 #include <sys/ioctl.h>
20 #include <linux/types.h>
21 #include <linux/videodev.h>
22 #include "videodev_mjpeg.h"
24 #include "config.h"
26 #include "video_out.h"
27 #include "video_out_internal.h"
28 #include "mp_msg.h"
29 #include "subopt-helper.h"
30 #include "fastmemcpy.h"
32 static vo_info_t info = {
33 "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
34 "zr2",
35 "Rik Snel <rsnel@cube.dyndns.org>",
39 LIBVO_EXTERN(zr2)
41 typedef struct {
42 /* options */
43 char *subdevice;
45 /* information for (and about) the zoran card */
47 unsigned char *buf; /* the JPEGs will be placed here */
48 struct mjpeg_requestbuffers zrq; /* info about this buffer */
50 int vdes; /* file descriptor of card */
51 int playing; /* 0 or 1 */
52 int frame, sync, queue; /* buffer management */
53 struct mjpeg_sync zs; /* state information */
54 struct mjpeg_params zp;
55 struct video_capability vc; /* max resolution and so on */
56 } vo_zr2_priv_t;
58 static vo_zr2_priv_t priv;
60 #define ZR2_MJPEG_NBUFFERS 2
61 #define ZR2_MJPEG_SIZE 1024*256
63 /* some convenient #define's, is this portable enough? */
64 #define DBG2(...) mp_msg(MSGT_VO, MSGL_DBG2, "vo_zr2: " __VA_ARGS__)
65 #define VERBOSE(...) mp_msg(MSGT_VO, MSGL_V, "vo_zr2: " __VA_ARGS__)
66 #define ERROR(...) mp_msg(MSGT_VO, MSGL_ERR, "vo_zr2: " __VA_ARGS__)
67 #define WARNING(...) mp_msg(MSGT_VO, MSGL_WARN, "vo_zr2: " __VA_ARGS__)
69 static void stop_playing(vo_zr2_priv_t *p) {
70 if (p->playing) {
71 p->frame = -1;
72 if (ioctl(p->vdes, MJPIOC_QBUF_PLAY, &p->frame) < 0)
73 ERROR("error stopping playback\n");
74 p->playing = 0;
75 p->sync = 0;
76 p->queue = 0;
77 p->frame = 0;
81 static const char *guess_device(const char *suggestion, int inform) {
82 struct stat vstat;
83 int res;
84 char *devs[] = {
85 "/dev/video",
86 "/dev/video0",
87 "/dev/v4l/video0",
88 "/dev/v4l0",
89 "/dev/v4l",
90 NULL
92 char **dev = devs;
94 if (suggestion) {
95 if (!*suggestion) {
96 ERROR("error: specified device name is empty string\n");
97 return NULL;
100 res = stat(suggestion, &vstat);
101 if (res == 0 && S_ISCHR(vstat.st_mode)) {
102 if (inform) VERBOSE("using device %s\n", suggestion);
103 return suggestion;
104 } else {
105 if (res != 0) ERROR("%s does not exist\n", suggestion);
106 else ERROR("%s is no character device\n", suggestion);
107 /* don't try to be smarter than the user, just exit */
108 return NULL;
112 while (*(++dev) != NULL) {
113 if (stat(*dev, &vstat) == 0 && S_ISCHR(vstat.st_mode)) {
114 VERBOSE("guessed video device %s\n", *dev);
115 return *dev;
117 dev++;
120 ERROR("unable to find video device\n");
122 return NULL;
125 static int query_format(uint32_t format) {
126 if (format==IMGFMT_ZRMJPEGNI ||
127 format==IMGFMT_ZRMJPEGIT ||
128 format==IMGFMT_ZRMJPEGIB)
129 return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW;
130 return 0;
133 static uint32_t draw_image(mp_image_t *mpi) {
134 vo_zr2_priv_t *p = &priv;
135 int size = (int)mpi->planes[1];
136 if (size > (int)p->zrq.size) {
137 ERROR("incoming JPEG image (size=%d) doesn't fit in buffer\n",
138 size);
139 return VO_FALSE;
142 /* looking for free buffer */
143 if (p->queue - p->sync < (int)p->zrq.count) p->frame = p->queue;
144 else {
145 if (ioctl(p->vdes, MJPIOC_SYNC, &p->zs) < 0) {
146 ERROR("error waiting for buffer to become free\n");
147 return VO_FALSE;
149 p->frame = p->zs.frame;
150 p->sync++;
153 /* copy the jpeg image to the buffer which we acquired */
154 memcpy(p->buf + p->zrq.size*p->frame, mpi->planes[0], size);
156 return VO_TRUE;
159 static const char *normstring(int norm) {
160 switch (norm) {
161 case VIDEO_MODE_PAL:
162 return "PAL";
163 case VIDEO_MODE_NTSC:
164 return "NTSC";
165 case VIDEO_MODE_SECAM:
166 return "SECAM";
167 case VIDEO_MODE_AUTO:
168 return "auto";
170 return "undefined";
173 static int get_norm(const char *n) {
174 if (!strcmp(n, "PAL")) return VIDEO_MODE_PAL;
175 if (!strcmp(n, "NTSC")) return VIDEO_MODE_NTSC;
176 if (!strcmp(n, "SECAM")) return VIDEO_MODE_SECAM;
177 if (!strcmp(n, "auto")) return VIDEO_MODE_AUTO;
178 return -1; /* invalid */
181 static int nc(const char **norm) {
182 if (get_norm(*norm) == -1) {
183 ERROR("norm \"%s\" is not supported, choose from PAL, NTSC, SECAM and auto\n", *norm);
184 return 0;
185 } else return 1;
188 static int pbc(int *prebuf) {
189 if (*prebuf) WARNING("prebuffering is not yet supported\n");
190 return 1;
193 static int preinit(const char *arg) {
194 vo_zr2_priv_t *p = &priv;
195 const char *dev = NULL;
196 char *dev_arg = NULL, *norm_arg = NULL;
197 int norm = VIDEO_MODE_AUTO, prebuf = 0;
198 opt_t subopts[] = { /* don't want warnings with -Wall... */
199 { "dev", OPT_ARG_MSTRZ, &dev_arg, NULL, 0 },
200 { "prebuf", OPT_ARG_BOOL, &prebuf, (opt_test_f)pbc, 0 },
201 { "norm", OPT_ARG_MSTRZ, &norm_arg, (opt_test_f)nc, 0 },
202 { NULL, 0, NULL, NULL, 0 }
205 VERBOSE("preinit() called with arg: %s\n", arg);
206 memset(p, 0, sizeof(*p)); /* set defaults */
207 p->vdes = -1;
209 if (subopt_parse(arg, subopts)) {
210 mp_msg(MSGT_VO, MSGL_FATAL,
211 "Allowed suboptions for -vo zr2 are:\n"
212 "- dev=DEVICE (default: %s)\n"
213 "- norm=PAL|NTSC|SECAM|auto (default: auto)\n"
214 "- prebuf/noprebuf (default:"
215 " noprebuf)\n"
216 "\n"
217 "Example: mplayer -vo zr2:dev=/dev/video1:"
218 "norm=PAL movie.avi\n\n"
219 , guess_device(NULL, 0));
220 free(norm_arg);
221 free(dev_arg);
222 return -1;
225 /* interpret the strings we got from subopt_parse */
226 if (norm_arg) {
227 norm = get_norm(norm_arg);
228 free(norm_arg);
231 if (dev_arg) dev = dev_arg;
233 dev = guess_device(dev, 1);
234 if (!dev) {
235 free(dev_arg);
236 uninit();
237 return 1;
240 p->vdes = open(dev, O_RDWR);
241 if (p->vdes < 0) {
242 ERROR("error opening %s: %s\n", dev, strerror(errno));
243 free(dev_arg);
244 uninit();
245 return 1;
248 free(dev_arg);
250 /* check if we really are dealing with a zoran card */
251 if (ioctl(p->vdes, MJPIOC_G_PARAMS, &p->zp) < 0) {
252 ERROR("%s probably is not a DC10(+)/buz/lml33\n", dev);
253 uninit();
254 return 1;
257 VERBOSE("kernel driver version %d.%d, current norm is %s\n",
258 p->zp.major_version, p->zp.minor_version,
259 normstring(p->zp.norm));
261 /* changing the norm in the zoran_params and MJPIOC_S_PARAMS
262 * does nothing the last time I tried, so bail out if the norm
263 * is not correct */
264 if (norm != VIDEO_MODE_AUTO && p->zp.norm != norm) {
265 ERROR("mplayer currently can't change the video norm, "
266 "change it with (eg.) XawTV and retry.\n");
267 uninit();
268 return 1;
271 /* gather useful information */
272 if (ioctl(p->vdes, VIDIOCGCAP, &p->vc) < 0) {
273 ERROR("error getting video capabilities from %s\n", dev);
274 uninit();
275 return 1;
278 VERBOSE("card reports maxwidth=%d, maxheight=%d\n",
279 p->vc.maxwidth, p->vc.maxheight);
281 /* according to the mjpegtools source, some cards return a bogus
282 * vc.maxwidth, correct it here. If a new zoran card appears with a
283 * maxwidth different 640, 720 or 768 this code may lead to problems */
284 if (p->vc.maxwidth != 640 && p->vc.maxwidth != 768) {
285 VERBOSE("card probably reported bogus width (%d), "
286 "changing to 720\n", p->vc.maxwidth);
287 p->vc.maxwidth = 720;
290 p->zrq.count = ZR2_MJPEG_NBUFFERS;
291 p->zrq.size = ZR2_MJPEG_SIZE;
293 if (ioctl(p->vdes, MJPIOC_REQBUFS, &p->zrq)) {
294 ERROR("error requesting %d buffers of size %d\n",
295 ZR2_MJPEG_NBUFFERS, ZR2_MJPEG_NBUFFERS);
296 uninit();
297 return 1;
300 VERBOSE("got %ld buffers of size %ld (wanted %d buffers of size %d)\n",
301 p->zrq.count, p->zrq.size, ZR2_MJPEG_NBUFFERS,
302 ZR2_MJPEG_SIZE);
304 p->buf = (unsigned char*)mmap(0, p->zrq.count*p->zrq.size,
305 PROT_READ|PROT_WRITE, MAP_SHARED, p->vdes, 0);
307 if (p->buf == MAP_FAILED) {
308 ERROR("error mapping requested buffers: %s", strerror(errno));
309 uninit();
310 return 1;
313 return 0;
316 static int config(uint32_t width, uint32_t height, uint32_t d_width,
317 uint32_t d_height, uint32_t flags, char *title, uint32_t format) {
318 int fields = 1, top_first = 1, err = 0;
319 int stretchx = 1, stretchy = 1;
320 struct mjpeg_params zptmp;
321 vo_zr2_priv_t *p = &priv;
322 VERBOSE("config() called\n");
324 /* paranoia check */
325 if (!query_format(format)) {
326 ERROR("called with wrong format, should be impossible\n");
327 return 1;
330 if ((int)height > p->vc.maxheight) {
331 ERROR("input height %d is too large, maxheight=%d\n",
332 height, p->vc.maxheight);
333 err = 1;
336 if (format != IMGFMT_ZRMJPEGNI) {
337 fields = 2;
338 if (format == IMGFMT_ZRMJPEGIB)
339 top_first = 0;
340 } else if ((int)height > p->vc.maxheight/2) {
341 ERROR("input is too high (%d) for non-interlaced playback"
342 "max=%d\n", height, p->vc.maxheight);
343 err = 1;
346 if (width%16 != 0) {
347 ERROR("input width=%d, must be multiple of 16\n", width);
348 err = 1;
351 if (height%(fields*8) != 0) {
352 ERROR("input height=%d, must be multiple of %d\n",
353 height, 2*fields);
354 err = 1;
357 /* we assume sample_aspect = 1 */
358 if (fields == 1) {
359 if (2*d_width <= (uint32_t)p->vc.maxwidth) {
360 VERBOSE("stretching x direction to preserve aspect\n");
361 d_width *= 2;
362 } else VERBOSE("unable to preserve aspect, screen width "
363 "too small\n");
366 if (d_width == width) stretchx = 1;
367 else if (d_width == 2*width) stretchx = 2;
368 #if 0 /* do minimal stretching for now */
369 else if (d_width == 4*width) stretchx = 4;
370 else WARNING("d_width must be {1,2,4}*width, using defaults\n");
372 if (d_height == height) stretchy = 1;
373 else if (d_height == 2*height) stretchy = 2;
374 else if (d_height == 4*height) stretchy = 4;
375 else WARNING("d_height must be {1,2,4}*height, using defaults\n");
376 #endif
378 if (stretchx*width > (uint32_t)p->vc.maxwidth) {
379 ERROR("movie to be played is too wide, width=%d>maxwidth=%d\n",
380 width*stretchx, p->vc.maxwidth);
381 err = 1;
384 if (stretchy*height > (uint32_t)p->vc.maxheight) {
385 ERROR("movie to be played is too heigh, height=%d>maxheight"
386 "=%d\n", height*stretchy, p->vc.maxheight);
387 err = 1;
390 if (err == 1) return 1;
392 /* some video files (eg. concatenated MPEG files), make MPlayer
393 * call config() during playback while no parameters have changed.
394 * We make configuration changes to a temporary params structure,
395 * compare it with the old params structure and only apply the new
396 * config if it is different from the old one. */
397 memcpy(&zptmp, &p->zp, sizeof(zptmp));
399 /* translate the configuration to zoran understandable format */
400 zptmp.decimation = 0;
401 zptmp.HorDcm = stretchx;
402 zptmp.VerDcm = stretchy;
403 zptmp.TmpDcm = 1;
404 zptmp.field_per_buff = fields;
405 zptmp.odd_even = top_first;
407 /* center the image on screen */
408 zptmp.img_x = (p->vc.maxwidth - width*stretchx)/2;
409 zptmp.img_y = (p->vc.maxheight - height*stretchy*(3-fields))/4;
411 zptmp.img_width = stretchx*width;
412 zptmp.img_height = stretchy*height/fields;
414 VERBOSE("tv: %dx%d, out: %dx%d+%d+%d, in: %ux%u %s%s%s\n",
415 p->vc.maxwidth, p->vc.maxheight,
416 zptmp.img_width, 2*zptmp.img_height,
417 zptmp.img_x, 2*zptmp.img_y,
418 width, height, (fields == 1) ? "non-interlaced" : "",
419 (fields == 2 && top_first == 1)
420 ? "interlaced top first" : "",
421 (fields == 2 && top_first == 0)
422 ? "interlaced bottom first" : "");
424 if (memcmp(&zptmp, &p->zp, sizeof(zptmp))) {
425 /* config differs, we must update */
426 memcpy(&p->zp, &zptmp, sizeof(zptmp));
427 stop_playing(p);
428 if (ioctl(p->vdes, MJPIOC_S_PARAMS, &p->zp) < 0) {
429 ERROR("error writing display params to card\n");
430 return 1;
432 VERBOSE("successfully written display parameters to card\n");
433 } else VERBOSE("config didn't change, no need to write it to card\n");
435 return 0;
438 static int control(uint32_t request, void *data, ...) {
439 switch (request) {
440 case VOCTRL_QUERY_FORMAT:
441 return query_format(*((uint32_t*)data));
442 case VOCTRL_DRAW_IMAGE:
443 return draw_image(data);
445 return VO_NOTIMPL;
448 static int draw_frame(uint8_t *src[]) {
449 return 0;
452 static int draw_slice(uint8_t *image[], int stride[],
453 int w, int h, int x, int y) {
454 return 0;
457 static void draw_osd(void) {
460 static void flip_page(void) {
461 vo_zr2_priv_t *p = &priv;
462 /* queueing the buffer for playback */
463 /* queueing the first buffer automatically starts playback */
464 if (p->playing == 0) p->playing = 1;
465 if (ioctl(p->vdes, MJPIOC_QBUF_PLAY, &p->frame) < 0)
466 ERROR("error queueing buffer for playback\n");
467 else p->queue++;
470 static void check_events(void) {
473 static void uninit(void) {
474 vo_zr2_priv_t *p = &priv;
475 VERBOSE("uninit() called (may be called from preinit() on error)\n");
477 stop_playing(p);
479 if (p->buf && munmap(p->buf, p->zrq.size*p->zrq.count))
480 ERROR("error munmapping buffer: %s\n", strerror(errno));
482 if (p->vdes >= 0) close(p->vdes);
483 free(p->subdevice);