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