Mercurial > mplayer.hg
comparison libvo/vo_zr2.c @ 11390:32eb3dfe44c9
new zoran driver as discussed on the CVS list. Hardware passthrough is
currently supported. Testing:
mplayer file.avi -vc zrmjpeg -vo zr2 -v
author | rik |
---|---|
date | Tue, 04 Nov 2003 20:05:47 +0000 |
parents | |
children | af8c66f215cf |
comparison
equal
deleted
inserted
replaced
11389:0033828bd0f2 | 11390:32eb3dfe44c9 |
---|---|
1 /* | |
2 * vo_zr2.c - playback on zoran cards | |
3 * Based on vo_zr.c,v 1.27 | |
4 * Copyright (C) Rik Snel 2001-2003, License GNU GPL v2 | |
5 */ | |
6 | |
7 /* $Id$ */ | |
8 | |
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" | |
23 | |
24 #include "config.h" | |
25 | |
26 #include "video_out.h" | |
27 #include "video_out_internal.h" | |
28 #include "../mp_msg.h" | |
29 #include "fastmemcpy.h" | |
30 | |
31 static vo_info_t info = { | |
32 "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)", | |
33 "zr2", | |
34 "Rik Snel <snel@phys.uu.nl>", | |
35 "" | |
36 }; | |
37 | |
38 LIBVO_EXTERN(zr2) | |
39 | |
40 typedef struct { | |
41 /* information for (and about) the zoran card */ | |
42 | |
43 unsigned char *buf; /* the JPEGs will be placed here */ | |
44 struct mjpeg_requestbuffers zrq; /* info about this buffer */ | |
45 | |
46 int vdes; /* file descriptor of card */ | |
47 int playing; /* 0 or 1 */ | |
48 int frame, sync, queue; /* buffer management */ | |
49 struct mjpeg_sync zs; /* state information */ | |
50 struct mjpeg_params zp; | |
51 struct video_capability vc; /* max resolution and so on */ | |
52 } vo_zr2_priv_t; | |
53 | |
54 static vo_zr2_priv_t priv; | |
55 | |
56 #define ZR2_MJPEG_NBUFFERS 2 | |
57 #define ZR2_MJPEG_SIZE 1024*256 | |
58 | |
59 /* some convenient #define's, is this portable enough? */ | |
60 #define VERBOSE(...) mp_msg(MSGT_VO, MSGL_V, "vo_zr2: " __VA_ARGS__) | |
61 #define ERROR(...) mp_msg(MSGT_VO, MSGL_ERR, "vo_zr2: " __VA_ARGS__) | |
62 #define WARNING(...) mp_msg(MSGT_VO, MSGL_WARN, "vo_zr2: " __VA_ARGS__) | |
63 | |
64 static void stop_playing(vo_zr2_priv_t *p) { | |
65 if (p->playing) { | |
66 p->frame = -1; | |
67 if (ioctl(p->vdes, MJPIOC_QBUF_PLAY, &p->frame) < 0) | |
68 ERROR("error stopping playback\n"); | |
69 p->playing = 0; | |
70 p->sync = 0; | |
71 p->queue = 0; | |
72 p->frame = 0; | |
73 } | |
74 } | |
75 | |
76 static char *guess_device(char *suggestion) { | |
77 struct stat vstat; | |
78 char *devs[] = { | |
79 "/dev/video", | |
80 "/dev/video0", | |
81 "/dev/v4l/video0", | |
82 "/dev/v4l0", | |
83 "/dev/v4l", | |
84 NULL | |
85 }; | |
86 char **dev = devs; | |
87 | |
88 if (suggestion) return suggestion; | |
89 | |
90 while (*(++dev) != NULL) { | |
91 if ((stat(*dev, &vstat) == 0) && S_ISCHR(vstat.st_mode)) { | |
92 VERBOSE("guessed video device %s\n", *dev); | |
93 return *dev; | |
94 } | |
95 dev++; | |
96 } | |
97 | |
98 ERROR("unable to find video device\n"); | |
99 | |
100 return NULL; | |
101 } | |
102 | |
103 static uint32_t query_format(uint32_t format) { | |
104 if (format==IMGFMT_ZRMJPEGNI || | |
105 format==IMGFMT_ZRMJPEGIT || | |
106 format==IMGFMT_ZRMJPEGIB) | |
107 return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW; | |
108 return 0; | |
109 } | |
110 | |
111 static uint32_t draw_image(mp_image_t *mpi) { | |
112 vo_zr2_priv_t *p = &priv; | |
113 int size = (int)mpi->planes[1]; | |
114 if (size > p->zrq.size) { | |
115 ERROR("incoming JPEG image (size=%d) doesn't fit in buffer\n", | |
116 size); | |
117 return VO_FALSE; | |
118 } | |
119 | |
120 /* looking for free buffer */ | |
121 if (p->queue - p->sync < p->zrq.count) p->frame = p->queue; | |
122 else { | |
123 if (ioctl(p->vdes, MJPIOC_SYNC, &p->zs) < 0) { | |
124 ERROR("error waiting for buffer to become free\n"); | |
125 return VO_FALSE; | |
126 } | |
127 p->frame = p->zs.frame; | |
128 p->sync++; | |
129 } | |
130 | |
131 /* copy the jpeg image to the buffer which we acquired */ | |
132 memcpy(p->buf + p->zrq.size*p->frame, mpi->planes[0], size); | |
133 | |
134 return VO_TRUE; | |
135 } | |
136 | |
137 static uint32_t preinit(const char *arg) { | |
138 char *dev; | |
139 vo_zr2_priv_t *p = &priv; | |
140 | |
141 VERBOSE("preinit() called\n"); | |
142 memset(p, 0, sizeof(*p)); /* set defaults */ | |
143 | |
144 if (arg) { | |
145 ERROR("no subdevice parameters supported yet: %s\n",arg); | |
146 return 1; | |
147 } | |
148 | |
149 dev = guess_device(NULL); | |
150 if (!dev) return 1; | |
151 | |
152 p->vdes = open(dev, O_RDWR); | |
153 if (p->vdes < 0) { | |
154 ERROR("error opening %s: %s\n", dev, strerror(errno)); | |
155 return 1; | |
156 } | |
157 | |
158 /* check if we really are dealing with a zoran card */ | |
159 if (ioctl(p->vdes, MJPIOC_G_PARAMS, &p->zp) < 0) { | |
160 ERROR("%s probably is not a DC10(+)/buz/lml33\n", dev); | |
161 return 1; | |
162 } | |
163 | |
164 VERBOSE("kernel driver version %d.%d, current norm is %s\n", | |
165 p->zp.major_version, p->zp.minor_version, | |
166 p->zp.norm == VIDEO_MODE_PAL ? "PAL" : "NTSC"); | |
167 | |
168 /* gather useful information */ | |
169 if (ioctl(p->vdes, VIDIOCGCAP, &p->vc) < 0) { | |
170 ERROR("error getting video capabilities from %s\n", dev); | |
171 return 1; | |
172 } | |
173 | |
174 VERBOSE("card reports maxwidth=%d, maxheight=%d\n", | |
175 p->vc.maxwidth, p->vc.maxheight); | |
176 | |
177 p->zrq.count = ZR2_MJPEG_NBUFFERS; | |
178 p->zrq.size = ZR2_MJPEG_SIZE; | |
179 | |
180 if (ioctl(p->vdes, MJPIOC_REQBUFS, &p->zrq)) { | |
181 ERROR("error requesting %d buffers of size %d\n", | |
182 ZR2_MJPEG_NBUFFERS, ZR2_MJPEG_NBUFFERS); | |
183 return 1; | |
184 } | |
185 | |
186 VERBOSE("got %ld buffers of size %ld (wanted %d buffers of size %d)\n", | |
187 p->zrq.count, p->zrq.size, ZR2_MJPEG_NBUFFERS, | |
188 ZR2_MJPEG_SIZE); | |
189 | |
190 p->buf = (unsigned char*)mmap(0, p->zrq.count*p->zrq.size, | |
191 PROT_READ|PROT_WRITE, MAP_SHARED, p->vdes, 0); | |
192 | |
193 if (p->buf == MAP_FAILED) { | |
194 ERROR("error mapping requested buffers: %s", strerror(errno)); | |
195 return 1; | |
196 } | |
197 | |
198 return 0; | |
199 } | |
200 | |
201 static uint32_t config(uint32_t width, uint32_t height, uint32_t d_width, | |
202 uint32_t d_height, uint32_t flags, char *title, uint32_t format) { | |
203 int fields = 1, top_first = 1, err = 0; | |
204 int stretchx = 1, stretchy = 1; | |
205 struct mjpeg_params zptmp; | |
206 vo_zr2_priv_t *p = &priv; | |
207 VERBOSE("config() called\n"); | |
208 | |
209 /* paranoia check */ | |
210 if (!query_format(format)) { | |
211 ERROR("called with wrong format, should be impossible\n"); | |
212 return 1; | |
213 } | |
214 | |
215 if ((int)height > p->vc.maxheight) { | |
216 ERROR("input height %d is too large, maxheight=%d\n", | |
217 height, p->vc.maxheight); | |
218 err = 1; | |
219 } | |
220 | |
221 if (format != IMGFMT_ZRMJPEGNI) { | |
222 fields = 2; | |
223 if (format == IMGFMT_ZRMJPEGIB) | |
224 top_first = 0; | |
225 } else if ((int)height > p->vc.maxheight/2) { | |
226 ERROR("input is too high (%d) for non-interlaced playback" | |
227 "max=%d\n", height, p->vc.maxheight); | |
228 err = 1; | |
229 } | |
230 | |
231 if (width%16 != 0) { | |
232 ERROR("input width=%d, must be multiple of 16\n", width); | |
233 err = 1; | |
234 } | |
235 | |
236 if (height%(fields*8) != 0) { | |
237 ERROR("input height=%d, must be multiple of %d\n", | |
238 height, 2*fields); | |
239 err = 1; | |
240 } | |
241 | |
242 /* we assume sample_aspect = 1 */ | |
243 if (fields == 1) { | |
244 if (2*d_width <= p->vc.maxwidth) { | |
245 VERBOSE("stretching x direction to preserve aspect\n"); | |
246 d_width *= 2; | |
247 } else VERBOSE("unable to preserve aspect, screen width " | |
248 "too small\n"); | |
249 } | |
250 | |
251 if (d_width == width) stretchx = 1; | |
252 else if (d_width == 2*width) stretchx = 2; | |
253 #if 0 /* do minimal stretching for now */ | |
254 else if (d_width == 4*width) stretchx = 4; | |
255 else WARNING("d_width must be {1,2,4}*width, using defaults\n"); | |
256 | |
257 if (d_height == height) stretchy = 1; | |
258 else if (d_height == 2*height) stretchy = 2; | |
259 else if (d_height == 4*height) stretchy = 4; | |
260 else WARNING("d_height must be {1,2,4}*height, using defaults\n"); | |
261 #endif | |
262 | |
263 if (stretchx*width > p->vc.maxwidth) { | |
264 ERROR("movie to be played is too wide, width=%d>maxwidth=%d", | |
265 width*stretchx, p->vc.maxwidth); | |
266 err = 1; | |
267 } | |
268 | |
269 if (stretchy*height > p->vc.maxheight) { | |
270 ERROR("movie to be played is too heigh, height=%d>maxheight=%d", | |
271 height*stretchy, p->vc.maxheight); | |
272 err = 1; | |
273 } | |
274 | |
275 if (err == 1) return 1; | |
276 | |
277 /* some video files (eg. concatenated MPEG files), make MPlayer | |
278 * call config() during playback while no parameters have changed. | |
279 * We make configuration changes to a temporary params structure, | |
280 * compare it with the old params structure and only apply the new | |
281 * config if it is different from the old one. */ | |
282 memcpy(&zptmp, &p->zp, sizeof(zptmp)); | |
283 | |
284 /* translate the configuration to zoran understandable format */ | |
285 zptmp.decimation = 0; | |
286 zptmp.HorDcm = stretchx; | |
287 zptmp.VerDcm = stretchy; | |
288 zptmp.TmpDcm = 1; | |
289 zptmp.field_per_buff = fields; | |
290 zptmp.odd_even = top_first; | |
291 | |
292 /* center the image on screen */ | |
293 zptmp.img_x = (p->vc.maxwidth - width*stretchx)/2; | |
294 zptmp.img_y = (p->vc.maxheight - height*stretchy*(3-fields))/4; | |
295 | |
296 zptmp.img_width = stretchx*width; | |
297 zptmp.img_height = stretchy*height/fields; | |
298 | |
299 VERBOSE("tv: %dx%d, out: %dx%d+%d+%d, in: %ux%u %s%s%s\n", | |
300 p->vc.maxwidth, p->vc.maxheight, | |
301 zptmp.img_width, 2*zptmp.img_height, | |
302 zptmp.img_x, zptmp.img_y*(fields - 3), | |
303 width, height, (fields == 1) ? "non-interlaced" : "", | |
304 (fields == 2 && top_first == 1) | |
305 ? "interlaced top first" : "", | |
306 (fields == 2 && top_first == 0) | |
307 ? "interlaced bottom first" : ""); | |
308 | |
309 if (memcmp(&zptmp, &p->zp, sizeof(zptmp))) { | |
310 /* config differs, we must update */ | |
311 memcpy(&p->zp, &zptmp, sizeof(zptmp)); | |
312 stop_playing(p); | |
313 if (ioctl(p->vdes, MJPIOC_S_PARAMS, &p->zp) < 0) { | |
314 ERROR("error writing display params to card\n"); | |
315 return 1; | |
316 } | |
317 VERBOSE("successfully written display parameters to card\n"); | |
318 } else VERBOSE("config didn't change, no need to write it to card\n"); | |
319 | |
320 return 0; | |
321 } | |
322 | |
323 static uint32_t control(uint32_t request, void *data, ...) { | |
324 switch (request) { | |
325 case VOCTRL_QUERY_FORMAT: | |
326 return query_format(*((uint32_t*)data)); | |
327 case VOCTRL_DRAW_IMAGE: | |
328 return draw_image(data); | |
329 } | |
330 return VO_NOTIMPL; | |
331 } | |
332 | |
333 static uint32_t draw_frame(uint8_t *src[]) { | |
334 return 0; | |
335 } | |
336 | |
337 static uint32_t draw_slice(uint8_t *image[], int stride[], | |
338 int w, int h, int x, int y) { | |
339 return 0; | |
340 } | |
341 | |
342 static void draw_osd(void) { | |
343 } | |
344 | |
345 static void flip_page(void) { | |
346 vo_zr2_priv_t *p = &priv; | |
347 /* queueing the buffer for playback */ | |
348 /* queueing the first buffer automatically starts playback */ | |
349 if (p->playing == 0) p->playing = 1; | |
350 if (ioctl(p->vdes, MJPIOC_QBUF_PLAY, &p->frame) < 0) | |
351 ERROR("error queueing buffer for playback\n"); | |
352 else p->queue++; | |
353 } | |
354 | |
355 static void check_events(void) { | |
356 } | |
357 | |
358 static void uninit(void) { | |
359 vo_zr2_priv_t *p = &priv; | |
360 VERBOSE("uninit() called\n"); | |
361 | |
362 stop_playing(p); | |
363 | |
364 if (munmap(p->buf, p->zrq.size*p->zrq.count)) | |
365 ERROR("error munmapping buffer: %s\n", strerror(errno)); | |
366 | |
367 close(p->vdes); | |
368 } |