Use memset to initialize huge arrays.
[mplayer.git] / libvo / vo_zr2.c
blob9f086cc4e1de59eb452145d9388ba86a54e87117
1 /*
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.
23 /* $Id$ */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/mman.h>
35 #include <sys/ioctl.h>
36 #include <linux/types.h>
37 #include <linux/videodev.h>
38 #include "config.h"
39 #include "videodev_mjpeg.h"
40 #include "video_out.h"
41 #include "video_out_internal.h"
42 #include "mp_msg.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)",
48 "zr2",
49 "Rik Snel <rsnel@cube.dyndns.org>",
53 const LIBVO_EXTERN(zr2)
55 typedef struct {
56 /* options */
57 char *subdevice;
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 */
70 } vo_zr2_priv_t;
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) {
84 if (p->playing) {
85 p->frame = -1;
86 if (ioctl(p->vdes, MJPIOC_QBUF_PLAY, &p->frame) < 0)
87 ERROR("error stopping playback\n");
88 p->playing = 0;
89 p->sync = 0;
90 p->queue = 0;
91 p->frame = 0;
95 static const char *guess_device(const char *suggestion, int inform) {
96 struct stat vstat;
97 int res;
98 static const char * const devs[] = {
99 "/dev/video",
100 "/dev/video0",
101 "/dev/v4l/video0",
102 "/dev/v4l0",
103 "/dev/v4l",
104 NULL
106 const char * const *dev = devs;
108 if (suggestion) {
109 if (!*suggestion) {
110 ERROR("error: specified device name is empty string\n");
111 return NULL;
114 res = stat(suggestion, &vstat);
115 if (res == 0 && S_ISCHR(vstat.st_mode)) {
116 if (inform) VERBOSE("using device %s\n", suggestion);
117 return suggestion;
118 } else {
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 */
122 return NULL;
126 while (*(++dev) != NULL) {
127 if (stat(*dev, &vstat) == 0 && S_ISCHR(vstat.st_mode)) {
128 VERBOSE("guessed video device %s\n", *dev);
129 return *dev;
131 dev++;
134 ERROR("unable to find video device\n");
136 return NULL;
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;
144 return 0;
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",
152 size);
153 return VO_FALSE;
156 /* looking for free buffer */
157 if (p->queue - p->sync < (int)p->zrq.count) p->frame = p->queue;
158 else {
159 if (ioctl(p->vdes, MJPIOC_SYNC, &p->zs) < 0) {
160 ERROR("error waiting for buffer to become free\n");
161 return VO_FALSE;
163 p->frame = p->zs.frame;
164 p->sync++;
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);
170 return VO_TRUE;
173 static const char *normstring(int norm) {
174 switch (norm) {
175 case VIDEO_MODE_PAL:
176 return "PAL";
177 case VIDEO_MODE_NTSC:
178 return "NTSC";
179 case VIDEO_MODE_SECAM:
180 return "SECAM";
181 case VIDEO_MODE_AUTO:
182 return "auto";
184 return "undefined";
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);
199 return 0;
200 } else return 1;
203 static int pbc(void *prebufp) {
204 int *prebuf = prebufp;
205 if (*prebuf) WARNING("prebuffering is not yet supported\n");
206 return 1;
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 */
223 p->vdes = -1;
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:"
231 " noprebuf)\n"
232 "\n"
233 "Example: mplayer -vo zr2:dev=/dev/video1:"
234 "norm=PAL movie.avi\n\n"
235 , guess_device(NULL, 0));
236 free(norm_arg);
237 free(dev_arg);
238 return -1;
241 /* interpret the strings we got from subopt_parse */
242 if (norm_arg) {
243 norm = get_norm(norm_arg);
244 free(norm_arg);
247 if (dev_arg) dev = dev_arg;
249 dev = guess_device(dev, 1);
250 if (!dev) {
251 free(dev_arg);
252 uninit();
253 return 1;
256 p->vdes = open(dev, O_RDWR);
257 if (p->vdes < 0) {
258 ERROR("error opening %s: %s\n", dev, strerror(errno));
259 free(dev_arg);
260 uninit();
261 return 1;
264 free(dev_arg);
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);
269 uninit();
270 return 1;
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
279 * is not correct */
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");
283 uninit();
284 return 1;
287 /* gather useful information */
288 if (ioctl(p->vdes, VIDIOCGCAP, &p->vc) < 0) {
289 ERROR("error getting video capabilities from %s\n", dev);
290 uninit();
291 return 1;
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);
312 uninit();
313 return 1;
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,
318 ZR2_MJPEG_SIZE);
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));
325 uninit();
326 return 1;
329 return 0;
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");
340 /* paranoia check */
341 if (!query_format(format)) {
342 ERROR("called with wrong format, should be impossible\n");
343 return 1;
346 if ((int)height > p->vc.maxheight) {
347 ERROR("input height %d is too large, maxheight=%d\n",
348 height, p->vc.maxheight);
349 err = 1;
352 if (format != IMGFMT_ZRMJPEGNI) {
353 fields = 2;
354 if (format == IMGFMT_ZRMJPEGIB)
355 top_first = 0;
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);
359 err = 1;
362 if (width%16 != 0) {
363 ERROR("input width=%d, must be multiple of 16\n", width);
364 err = 1;
367 if (height%(fields*8) != 0) {
368 ERROR("input height=%d, must be multiple of %d\n",
369 height, 2*fields);
370 err = 1;
373 /* we assume sample_aspect = 1 */
374 if (fields == 1) {
375 if (2*d_width <= (uint32_t)p->vc.maxwidth) {
376 VERBOSE("stretching x direction to preserve aspect\n");
377 d_width *= 2;
378 } else VERBOSE("unable to preserve aspect, screen width "
379 "too small\n");
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");
392 #endif
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);
397 err = 1;
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);
403 err = 1;
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;
419 zptmp.TmpDcm = 1;
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));
443 stop_playing(p);
444 if (ioctl(p->vdes, MJPIOC_S_PARAMS, &p->zp) < 0) {
445 ERROR("error writing display params to card\n");
446 return 1;
448 VERBOSE("successfully written display parameters to card\n");
449 } else VERBOSE("config didn't change, no need to write it to card\n");
451 return 0;
454 static int control(uint32_t request, void *data, ...) {
455 switch (request) {
456 case VOCTRL_QUERY_FORMAT:
457 return query_format(*((uint32_t*)data));
458 case VOCTRL_DRAW_IMAGE:
459 return draw_image(data);
461 return VO_NOTIMPL;
464 static int draw_frame(uint8_t *src[]) {
465 return 0;
468 static int draw_slice(uint8_t *image[], int stride[],
469 int w, int h, int x, int y) {
470 return 0;
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");
483 else p->queue++;
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");
493 stop_playing(p);
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);
499 free(p->subdevice);