|
1 /* |
|
2 SDL - Simple DirectMedia Layer |
|
3 Copyright (C) 1997-2006 Sam Lantinga |
|
4 |
|
5 This library is free software; you can redistribute it and/or |
|
6 modify it under the terms of the GNU Lesser General Public |
|
7 License as published by the Free Software Foundation; either |
|
8 version 2.1 of the License, or (at your option) any later version. |
|
9 |
|
10 This library is distributed in the hope that it will be useful, |
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 Lesser General Public License for more details. |
|
14 |
|
15 You should have received a copy of the GNU Lesser General Public |
|
16 License along with this library; if not, write to the Free Software |
|
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
|
19 Sam Lantinga |
|
20 slouken@libsdl.org |
|
21 */ |
|
22 #include "SDL_config.h" |
|
23 |
|
24 /* This is the XFree86 Xv extension implementation of YUV video overlays */ |
|
25 |
|
26 #if SDL_VIDEO_DRIVER_X11_XV |
|
27 |
|
28 #include <X11/Xlib.h> |
|
29 #ifndef NO_SHARED_MEMORY |
|
30 #include <sys/ipc.h> |
|
31 #include <sys/shm.h> |
|
32 #include <X11/extensions/XShm.h> |
|
33 #endif |
|
34 #include "../Xext/extensions/Xvlib.h" |
|
35 |
|
36 #include "SDL_x11yuv_c.h" |
|
37 #include "../SDL_yuvfuncs.h" |
|
38 |
|
39 #define XFREE86_REFRESH_HACK |
|
40 #ifdef XFREE86_REFRESH_HACK |
|
41 #include "SDL_x11image_c.h" |
|
42 #endif |
|
43 |
|
44 /* Workaround when pitch != width */ |
|
45 #define PITCH_WORKAROUND |
|
46 |
|
47 /* Workaround intel i810 video overlay waiting with failing until the |
|
48 first Xv[Shm]PutImage call <sigh> */ |
|
49 #define INTEL_XV_BADALLOC_WORKAROUND |
|
50 |
|
51 /* Fix for the NVidia GeForce 2 - use the last available adaptor */ |
|
52 /*#define USE_LAST_ADAPTOR*/ /* Apparently the NVidia drivers are fixed */ |
|
53 |
|
54 /* The functions used to manipulate software video overlays */ |
|
55 static struct private_yuvhwfuncs x11_yuvfuncs = { |
|
56 X11_LockYUVOverlay, |
|
57 X11_UnlockYUVOverlay, |
|
58 X11_DisplayYUVOverlay, |
|
59 X11_FreeYUVOverlay |
|
60 }; |
|
61 |
|
62 struct private_yuvhwdata { |
|
63 int port; |
|
64 #ifndef NO_SHARED_MEMORY |
|
65 int yuv_use_mitshm; |
|
66 XShmSegmentInfo yuvshm; |
|
67 #endif |
|
68 SDL_NAME(XvImage) *image; |
|
69 }; |
|
70 |
|
71 |
|
72 static int (*X_handler)(Display *, XErrorEvent *) = NULL; |
|
73 |
|
74 #ifndef NO_SHARED_MEMORY |
|
75 /* Shared memory error handler routine */ |
|
76 static int shm_error; |
|
77 static int shm_errhandler(Display *d, XErrorEvent *e) |
|
78 { |
|
79 if ( e->error_code == BadAccess ) { |
|
80 shm_error = True; |
|
81 return(0); |
|
82 } else |
|
83 return(X_handler(d,e)); |
|
84 } |
|
85 #endif /* !NO_SHARED_MEMORY */ |
|
86 |
|
87 static int xv_error; |
|
88 static int xv_errhandler(Display *d, XErrorEvent *e) |
|
89 { |
|
90 if ( e->error_code == BadMatch ) { |
|
91 xv_error = True; |
|
92 return(0); |
|
93 } else |
|
94 return(X_handler(d,e)); |
|
95 } |
|
96 |
|
97 #ifdef INTEL_XV_BADALLOC_WORKAROUND |
|
98 static int intel_errhandler(Display *d, XErrorEvent *e) |
|
99 { |
|
100 if ( e->error_code == BadAlloc ) { |
|
101 xv_error = True; |
|
102 return(0); |
|
103 } else |
|
104 return(X_handler(d,e)); |
|
105 } |
|
106 |
|
107 static void X11_ClearYUVOverlay(SDL_Overlay *overlay) |
|
108 { |
|
109 int x,y; |
|
110 |
|
111 switch (overlay->format) |
|
112 { |
|
113 case SDL_YV12_OVERLAY: |
|
114 case SDL_IYUV_OVERLAY: |
|
115 for (y = 0; y < overlay->h; y++) |
|
116 memset(overlay->pixels[0] + y * overlay->pitches[0], |
|
117 0, overlay->w); |
|
118 |
|
119 for (y = 0; y < (overlay->h / 2); y++) |
|
120 { |
|
121 memset(overlay->pixels[1] + y * overlay->pitches[1], |
|
122 -128, overlay->w / 2); |
|
123 memset(overlay->pixels[2] + y * overlay->pitches[2], |
|
124 -128, overlay->w / 2); |
|
125 } |
|
126 break; |
|
127 case SDL_YUY2_OVERLAY: |
|
128 case SDL_YVYU_OVERLAY: |
|
129 for (y = 0; y < overlay->h; y++) |
|
130 { |
|
131 for (x = 0; x < overlay->w; x += 2) |
|
132 { |
|
133 Uint8 *pixel_pair = overlay->pixels[0] + |
|
134 y * overlay->pitches[0] + x * 2; |
|
135 pixel_pair[0] = 0; |
|
136 pixel_pair[1] = -128; |
|
137 pixel_pair[2] = 0; |
|
138 pixel_pair[3] = -128; |
|
139 } |
|
140 } |
|
141 break; |
|
142 case SDL_UYVY_OVERLAY: |
|
143 for (y = 0; y < overlay->h; y++) |
|
144 { |
|
145 for (x = 0; x < overlay->w; x += 2) |
|
146 { |
|
147 Uint8 *pixel_pair = overlay->pixels[0] + |
|
148 y * overlay->pitches[0] + x * 2; |
|
149 pixel_pair[0] = -128; |
|
150 pixel_pair[1] = 0; |
|
151 pixel_pair[2] = -128; |
|
152 pixel_pair[3] = 0; |
|
153 } |
|
154 } |
|
155 break; |
|
156 } |
|
157 } |
|
158 #endif |
|
159 |
|
160 SDL_Overlay *X11_CreateYUVOverlay(_THIS, int width, int height, Uint32 format, SDL_Surface *display) |
|
161 { |
|
162 SDL_Overlay *overlay; |
|
163 struct private_yuvhwdata *hwdata; |
|
164 int xv_port; |
|
165 unsigned int i, j, k; |
|
166 unsigned int adaptors; |
|
167 SDL_NAME(XvAdaptorInfo) *ainfo; |
|
168 int bpp; |
|
169 #ifndef NO_SHARED_MEMORY |
|
170 XShmSegmentInfo *yuvshm; |
|
171 #endif |
|
172 #ifdef INTEL_XV_BADALLOC_WORKAROUND |
|
173 int intel_adapter = False; |
|
174 #endif |
|
175 |
|
176 /* Look for the XVideo extension with a valid port for this format */ |
|
177 xv_port = -1; |
|
178 if ( (Success == SDL_NAME(XvQueryExtension)(GFX_Display, &j, &j, &j, &j, &j)) && |
|
179 (Success == SDL_NAME(XvQueryAdaptors)(GFX_Display, |
|
180 RootWindow(GFX_Display, SDL_Screen), |
|
181 &adaptors, &ainfo)) ) { |
|
182 #ifdef USE_LAST_ADAPTOR |
|
183 for ( i=0; i < adaptors; ++i ) |
|
184 #else |
|
185 for ( i=0; (i < adaptors) && (xv_port == -1); ++i ) |
|
186 #endif /* USE_LAST_ADAPTOR */ |
|
187 { |
|
188 /* Check to see if the visual can be used */ |
|
189 if ( BUGGY_XFREE86(<=, 4001) ) { |
|
190 int visual_ok = 0; |
|
191 for ( j=0; j<ainfo[i].num_formats; ++j ) { |
|
192 if ( ainfo[i].formats[j].visual_id == |
|
193 SDL_Visual->visualid ) { |
|
194 visual_ok = 1; |
|
195 break; |
|
196 } |
|
197 } |
|
198 if ( ! visual_ok ) { |
|
199 continue; |
|
200 } |
|
201 } |
|
202 #ifdef INTEL_XV_BADALLOC_WORKAROUND |
|
203 if ( !strcmp(ainfo[i].name, "Intel(R) Video Overla")) |
|
204 intel_adapter = True; |
|
205 else |
|
206 intel_adapter = False; |
|
207 #endif |
|
208 if ( (ainfo[i].type & XvInputMask) && |
|
209 (ainfo[i].type & XvImageMask) ) { |
|
210 int num_formats; |
|
211 SDL_NAME(XvImageFormatValues) *formats; |
|
212 formats = SDL_NAME(XvListImageFormats)(GFX_Display, |
|
213 ainfo[i].base_id, &num_formats); |
|
214 #ifdef USE_LAST_ADAPTOR |
|
215 for ( j=0; j < num_formats; ++j ) |
|
216 #else |
|
217 for ( j=0; (j < num_formats) && (xv_port == -1); ++j ) |
|
218 #endif /* USE_LAST_ADAPTOR */ |
|
219 { |
|
220 if ( (Uint32)formats[j].id == format ) { |
|
221 for ( k=0; k < ainfo[i].num_ports; ++k ) { |
|
222 if ( Success == SDL_NAME(XvGrabPort)(GFX_Display, ainfo[i].base_id+k, CurrentTime) ) { |
|
223 xv_port = ainfo[i].base_id+k; |
|
224 break; |
|
225 } |
|
226 } |
|
227 } |
|
228 } |
|
229 if ( formats ) { |
|
230 XFree(formats); |
|
231 } |
|
232 } |
|
233 } |
|
234 SDL_NAME(XvFreeAdaptorInfo)(ainfo); |
|
235 } |
|
236 |
|
237 /* Precalculate the bpp for the pitch workaround below */ |
|
238 switch (format) { |
|
239 /* Add any other cases we need to support... */ |
|
240 case SDL_YUY2_OVERLAY: |
|
241 case SDL_UYVY_OVERLAY: |
|
242 case SDL_YVYU_OVERLAY: |
|
243 bpp = 2; |
|
244 break; |
|
245 default: |
|
246 bpp = 1; |
|
247 break; |
|
248 } |
|
249 |
|
250 #if 0 |
|
251 /* |
|
252 * !!! FIXME: |
|
253 * "Here are some diffs for X11 and yuv. Note that the last part 2nd |
|
254 * diff should probably be a new call to XvQueryAdaptorFree with ainfo |
|
255 * and the number of adaptors, instead of the loop through like I did." |
|
256 * |
|
257 * ACHTUNG: This is broken! It looks like XvFreeAdaptorInfo does this |
|
258 * for you, so we end up with a double-free. I need to look at this |
|
259 * more closely... --ryan. |
|
260 */ |
|
261 for ( i=0; i < adaptors; ++i ) { |
|
262 if (ainfo[i].name != NULL) Xfree(ainfo[i].name); |
|
263 if (ainfo[i].formats != NULL) Xfree(ainfo[i].formats); |
|
264 } |
|
265 Xfree(ainfo); |
|
266 #endif |
|
267 |
|
268 if ( xv_port == -1 ) { |
|
269 SDL_SetError("No available video ports for requested format"); |
|
270 return(NULL); |
|
271 } |
|
272 |
|
273 /* Enable auto-painting of the overlay colorkey */ |
|
274 { |
|
275 static const char *attr[] = { "XV_AUTOPAINT_COLORKEY", "XV_AUTOPAINT_COLOURKEY" }; |
|
276 unsigned int i; |
|
277 |
|
278 SDL_NAME(XvSelectPortNotify)(GFX_Display, xv_port, True); |
|
279 X_handler = XSetErrorHandler(xv_errhandler); |
|
280 for ( i=0; i < sizeof(attr)/(sizeof attr[0]); ++i ) { |
|
281 Atom a; |
|
282 |
|
283 xv_error = False; |
|
284 a = XInternAtom(GFX_Display, attr[i], True); |
|
285 if ( a != None ) { |
|
286 SDL_NAME(XvSetPortAttribute)(GFX_Display, xv_port, a, 1); |
|
287 XSync(GFX_Display, True); |
|
288 if ( ! xv_error ) { |
|
289 break; |
|
290 } |
|
291 } |
|
292 } |
|
293 XSetErrorHandler(X_handler); |
|
294 SDL_NAME(XvSelectPortNotify)(GFX_Display, xv_port, False); |
|
295 } |
|
296 |
|
297 /* Create the overlay structure */ |
|
298 overlay = (SDL_Overlay *)SDL_malloc(sizeof *overlay); |
|
299 if ( overlay == NULL ) { |
|
300 SDL_NAME(XvUngrabPort)(GFX_Display, xv_port, CurrentTime); |
|
301 SDL_OutOfMemory(); |
|
302 return(NULL); |
|
303 } |
|
304 SDL_memset(overlay, 0, (sizeof *overlay)); |
|
305 |
|
306 /* Fill in the basic members */ |
|
307 overlay->format = format; |
|
308 overlay->w = width; |
|
309 overlay->h = height; |
|
310 |
|
311 /* Set up the YUV surface function structure */ |
|
312 overlay->hwfuncs = &x11_yuvfuncs; |
|
313 overlay->hw_overlay = 1; |
|
314 |
|
315 /* Create the pixel data and lookup tables */ |
|
316 hwdata = (struct private_yuvhwdata *)SDL_malloc(sizeof *hwdata); |
|
317 overlay->hwdata = hwdata; |
|
318 if ( hwdata == NULL ) { |
|
319 SDL_NAME(XvUngrabPort)(GFX_Display, xv_port, CurrentTime); |
|
320 SDL_OutOfMemory(); |
|
321 SDL_FreeYUVOverlay(overlay); |
|
322 return(NULL); |
|
323 } |
|
324 hwdata->port = xv_port; |
|
325 #ifndef NO_SHARED_MEMORY |
|
326 yuvshm = &hwdata->yuvshm; |
|
327 SDL_memset(yuvshm, 0, sizeof(*yuvshm)); |
|
328 hwdata->image = SDL_NAME(XvShmCreateImage)(GFX_Display, xv_port, format, |
|
329 0, width, height, yuvshm); |
|
330 #ifdef PITCH_WORKAROUND |
|
331 if ( hwdata->image != NULL && hwdata->image->pitches[0] != (width*bpp) ) { |
|
332 /* Ajust overlay width according to pitch */ |
|
333 XFree(hwdata->image); |
|
334 width = hwdata->image->pitches[0] / bpp; |
|
335 hwdata->image = SDL_NAME(XvShmCreateImage)(GFX_Display, xv_port, format, |
|
336 0, width, height, yuvshm); |
|
337 } |
|
338 #endif /* PITCH_WORKAROUND */ |
|
339 hwdata->yuv_use_mitshm = (hwdata->image != NULL); |
|
340 if ( hwdata->yuv_use_mitshm ) { |
|
341 yuvshm->shmid = shmget(IPC_PRIVATE, hwdata->image->data_size, |
|
342 IPC_CREAT | 0777); |
|
343 if ( yuvshm->shmid >= 0 ) { |
|
344 yuvshm->shmaddr = (char *)shmat(yuvshm->shmid, 0, 0); |
|
345 yuvshm->readOnly = False; |
|
346 if ( yuvshm->shmaddr != (char *)-1 ) { |
|
347 shm_error = False; |
|
348 X_handler = XSetErrorHandler(shm_errhandler); |
|
349 XShmAttach(GFX_Display, yuvshm); |
|
350 XSync(GFX_Display, True); |
|
351 XSetErrorHandler(X_handler); |
|
352 if ( shm_error ) |
|
353 shmdt(yuvshm->shmaddr); |
|
354 } else { |
|
355 shm_error = True; |
|
356 } |
|
357 shmctl(yuvshm->shmid, IPC_RMID, NULL); |
|
358 } else { |
|
359 shm_error = True; |
|
360 } |
|
361 if ( shm_error ) { |
|
362 XFree(hwdata->image); |
|
363 hwdata->yuv_use_mitshm = 0; |
|
364 } else { |
|
365 hwdata->image->data = yuvshm->shmaddr; |
|
366 } |
|
367 } |
|
368 if ( !hwdata->yuv_use_mitshm ) |
|
369 #endif /* NO_SHARED_MEMORY */ |
|
370 { |
|
371 hwdata->image = SDL_NAME(XvCreateImage)(GFX_Display, xv_port, format, |
|
372 0, width, height); |
|
373 |
|
374 #ifdef PITCH_WORKAROUND |
|
375 if ( hwdata->image != NULL && hwdata->image->pitches[0] != (width*bpp) ) { |
|
376 /* Ajust overlay width according to pitch */ |
|
377 XFree(hwdata->image); |
|
378 width = hwdata->image->pitches[0] / bpp; |
|
379 hwdata->image = SDL_NAME(XvCreateImage)(GFX_Display, xv_port, format, |
|
380 0, width, height); |
|
381 } |
|
382 #endif /* PITCH_WORKAROUND */ |
|
383 if ( hwdata->image == NULL ) { |
|
384 SDL_SetError("Couldn't create XVideo image"); |
|
385 SDL_FreeYUVOverlay(overlay); |
|
386 return(NULL); |
|
387 } |
|
388 hwdata->image->data = SDL_malloc(hwdata->image->data_size); |
|
389 if ( hwdata->image->data == NULL ) { |
|
390 SDL_OutOfMemory(); |
|
391 SDL_FreeYUVOverlay(overlay); |
|
392 return(NULL); |
|
393 } |
|
394 } |
|
395 |
|
396 /* Find the pitch and offset values for the overlay */ |
|
397 overlay->planes = hwdata->image->num_planes; |
|
398 overlay->pitches = (Uint16 *)SDL_malloc(overlay->planes * sizeof(Uint16)); |
|
399 overlay->pixels = (Uint8 **)SDL_malloc(overlay->planes * sizeof(Uint8 *)); |
|
400 if ( !overlay->pitches || !overlay->pixels ) { |
|
401 SDL_OutOfMemory(); |
|
402 SDL_FreeYUVOverlay(overlay); |
|
403 return(NULL); |
|
404 } |
|
405 for ( i=0; i<overlay->planes; ++i ) { |
|
406 overlay->pitches[i] = hwdata->image->pitches[i]; |
|
407 overlay->pixels[i] = (Uint8 *)hwdata->image->data + |
|
408 hwdata->image->offsets[i]; |
|
409 } |
|
410 |
|
411 #ifdef XFREE86_REFRESH_HACK |
|
412 /* Work around an XFree86 X server bug (?) |
|
413 We can't perform normal updates in windows that have video |
|
414 being output to them. See SDL_x11image.c for more details. |
|
415 */ |
|
416 X11_DisableAutoRefresh(this); |
|
417 #endif |
|
418 |
|
419 #ifdef INTEL_XV_BADALLOC_WORKAROUND |
|
420 /* HACK, GRRR sometimes (i810) creating the overlay succeeds, but the |
|
421 first call to XvShm[Put]Image to a mapped window fails with: |
|
422 "BadAlloc (insufficient resources for operation)". This happens with |
|
423 certain formats when the XvImage is too large to the i810's liking. |
|
424 |
|
425 We work around this by doing a test XvShm[Put]Image with a black |
|
426 Xv image, this may cause some flashing, so only do this check if we |
|
427 are running on an intel Xv-adapter. */ |
|
428 if (intel_adapter) |
|
429 { |
|
430 xv_error = False; |
|
431 X_handler = XSetErrorHandler(intel_errhandler); |
|
432 |
|
433 X11_ClearYUVOverlay(overlay); |
|
434 |
|
435 /* We set the destination height and width to 1 pixel to avoid |
|
436 putting a large black rectangle over the screen, thus |
|
437 strongly reducing possible flashing. */ |
|
438 #ifndef NO_SHARED_MEMORY |
|
439 if ( hwdata->yuv_use_mitshm ) { |
|
440 SDL_NAME(XvShmPutImage)(GFX_Display, hwdata->port, |
|
441 SDL_Window, SDL_GC, |
|
442 hwdata->image, |
|
443 0, 0, overlay->w, overlay->h, |
|
444 0, 0, 1, 1, False); |
|
445 } |
|
446 else |
|
447 #endif |
|
448 { |
|
449 SDL_NAME(XvPutImage)(GFX_Display, hwdata->port, |
|
450 SDL_Window, SDL_GC, |
|
451 hwdata->image, |
|
452 0, 0, overlay->w, overlay->h, |
|
453 0, 0, 1, 1); |
|
454 } |
|
455 XSync(GFX_Display, False); |
|
456 XSetErrorHandler(X_handler); |
|
457 |
|
458 if (xv_error) |
|
459 { |
|
460 X11_FreeYUVOverlay(this, overlay); |
|
461 return NULL; |
|
462 } |
|
463 /* Repair the (1 pixel worth of) damage we've just done */ |
|
464 X11_RefreshDisplay(this); |
|
465 } |
|
466 #endif |
|
467 |
|
468 /* We're all done.. */ |
|
469 return(overlay); |
|
470 } |
|
471 |
|
472 int X11_LockYUVOverlay(_THIS, SDL_Overlay *overlay) |
|
473 { |
|
474 return(0); |
|
475 } |
|
476 |
|
477 void X11_UnlockYUVOverlay(_THIS, SDL_Overlay *overlay) |
|
478 { |
|
479 return; |
|
480 } |
|
481 |
|
482 int X11_DisplayYUVOverlay(_THIS, SDL_Overlay *overlay, SDL_Rect *src, SDL_Rect *dst) |
|
483 { |
|
484 struct private_yuvhwdata *hwdata; |
|
485 |
|
486 hwdata = overlay->hwdata; |
|
487 |
|
488 #ifndef NO_SHARED_MEMORY |
|
489 if ( hwdata->yuv_use_mitshm ) { |
|
490 SDL_NAME(XvShmPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC, |
|
491 hwdata->image, |
|
492 src->x, src->y, src->w, src->h, |
|
493 dst->x, dst->y, dst->w, dst->h, False); |
|
494 } |
|
495 else |
|
496 #endif |
|
497 { |
|
498 SDL_NAME(XvPutImage)(GFX_Display, hwdata->port, SDL_Window, SDL_GC, |
|
499 hwdata->image, |
|
500 src->x, src->y, src->w, src->h, |
|
501 dst->x, dst->y, dst->w, dst->h); |
|
502 } |
|
503 XSync(GFX_Display, False); |
|
504 return(0); |
|
505 } |
|
506 |
|
507 void X11_FreeYUVOverlay(_THIS, SDL_Overlay *overlay) |
|
508 { |
|
509 struct private_yuvhwdata *hwdata; |
|
510 |
|
511 hwdata = overlay->hwdata; |
|
512 if ( hwdata ) { |
|
513 SDL_NAME(XvUngrabPort)(GFX_Display, hwdata->port, CurrentTime); |
|
514 #ifndef NO_SHARED_MEMORY |
|
515 if ( hwdata->yuv_use_mitshm ) { |
|
516 XShmDetach(GFX_Display, &hwdata->yuvshm); |
|
517 shmdt(hwdata->yuvshm.shmaddr); |
|
518 } |
|
519 #endif |
|
520 if ( hwdata->image ) { |
|
521 XFree(hwdata->image); |
|
522 } |
|
523 SDL_free(hwdata); |
|
524 } |
|
525 if ( overlay->pitches ) { |
|
526 SDL_free(overlay->pitches); |
|
527 overlay->pitches = NULL; |
|
528 } |
|
529 if ( overlay->pixels ) { |
|
530 SDL_free(overlay->pixels); |
|
531 overlay->pixels = NULL; |
|
532 } |
|
533 #ifdef XFREE86_REFRESH_HACK |
|
534 X11_EnableAutoRefresh(this); |
|
535 #endif |
|
536 } |
|
537 |
|
538 #endif /* SDL_VIDEO_DRIVER_X11_XV */ |