FreeRDP
Loading...
Searching...
No Matches
xf_tsmf.c
1
20#include <freerdp/config.h>
21
22#include <winpr/crt.h>
23
24#include <sys/ipc.h>
25#include <sys/shm.h>
26
27#include <X11/Xlib.h>
28#include <X11/Xutil.h>
29#include <X11/Xatom.h>
30#include <X11/extensions/XShm.h>
31
32#include <freerdp/log.h>
33#include <freerdp/client/tsmf.h>
34
35#include "xf_tsmf.h"
36#include "xf_utils.h"
37
38#ifdef WITH_XV
39
40#include <X11/extensions/Xv.h>
41#include <X11/extensions/Xvlib.h>
42
43static long xv_port = 0;
44
45struct xf_xv_context
46{
47 long xv_port;
48 Atom xv_colorkey_atom;
49 int xv_image_size;
50 int xv_shmid;
51 char* xv_shmaddr;
52 UINT32* xv_pixfmts;
53};
54typedef struct xf_xv_context xfXvContext;
55
56#define TAG CLIENT_TAG("x11")
57
58static BOOL xf_tsmf_is_format_supported(xfXvContext* xv, UINT32 pixfmt)
59{
60 if (!xv->xv_pixfmts)
61 return FALSE;
62
63 for (int i = 0; xv->xv_pixfmts[i]; i++)
64 {
65 if (xv->xv_pixfmts[i] == pixfmt)
66 return TRUE;
67 }
68
69 return FALSE;
70}
71
72static int xf_tsmf_xv_video_frame_event(TsmfClientContext* tsmf, TSMF_VIDEO_FRAME_EVENT* event)
73{
74 int x = 0;
75 int y = 0;
76 UINT32 width = 0;
77 UINT32 height = 0;
78 BYTE* data1 = NULL;
79 BYTE* data2 = NULL;
80 UINT32 pixfmt = 0;
81 UINT32 xvpixfmt = 0;
82 XvImage* image = NULL;
83 int colorkey = 0;
84 int numRects = 0;
85 xfContext* xfc = NULL;
86 xfXvContext* xv = NULL;
87 XRectangle* xrects = NULL;
88 XShmSegmentInfo shminfo;
89 BOOL converti420yv12 = FALSE;
90
91 if (!tsmf)
92 return -1;
93
94 xfc = (xfContext*)tsmf->custom;
95
96 if (!xfc)
97 return -1;
98
99 xv = (xfXvContext*)xfc->xv_context;
100
101 if (!xv)
102 return -1;
103
104 if (xv->xv_port == 0)
105 return -1001;
106
107 /* In case the player is minimized */
108 if (event->x < -2048 || event->y < -2048 || event->numVisibleRects == 0)
109 {
110 return -1002;
111 }
112
113 xrects = NULL;
114 numRects = event->numVisibleRects;
115
116 if (numRects > 0)
117 {
118 xrects = (XRectangle*)calloc(numRects, sizeof(XRectangle));
119
120 if (!xrects)
121 return -1;
122
123 for (int i = 0; i < numRects; i++)
124 {
125 x = event->x + event->visibleRects[i].left;
126 y = event->y + event->visibleRects[i].top;
127 width = event->visibleRects[i].right - event->visibleRects[i].left;
128 height = event->visibleRects[i].bottom - event->visibleRects[i].top;
129
130 xrects[i].x = x;
131 xrects[i].y = y;
132 xrects[i].width = width;
133 xrects[i].height = height;
134 }
135 }
136
137 if (xv->xv_colorkey_atom != None)
138 {
139 XvGetPortAttribute(xfc->display, xv->xv_port, xv->xv_colorkey_atom, &colorkey);
140 XSetFunction(xfc->display, xfc->gc, GXcopy);
141 XSetFillStyle(xfc->display, xfc->gc, FillSolid);
142 XSetForeground(xfc->display, xfc->gc, colorkey);
143
144 if (event->numVisibleRects < 1)
145 {
146 XSetClipMask(xfc->display, xfc->gc, None);
147 }
148 else
149 {
150 XFillRectangles(xfc->display, xfc->window->handle, xfc->gc, xrects, numRects);
151 }
152 }
153 else
154 {
155 XSetFunction(xfc->display, xfc->gc, GXcopy);
156 XSetFillStyle(xfc->display, xfc->gc, FillSolid);
157
158 if (event->numVisibleRects < 1)
159 {
160 XSetClipMask(xfc->display, xfc->gc, None);
161 }
162 else
163 {
164 XSetClipRectangles(xfc->display, xfc->gc, 0, 0, xrects, numRects, YXBanded);
165 }
166 }
167
168 pixfmt = event->framePixFmt;
169
170 if (xf_tsmf_is_format_supported(xv, pixfmt))
171 {
172 xvpixfmt = pixfmt;
173 }
174 else if (pixfmt == RDP_PIXFMT_I420 && xf_tsmf_is_format_supported(xv, RDP_PIXFMT_YV12))
175 {
176 xvpixfmt = RDP_PIXFMT_YV12;
177 converti420yv12 = TRUE;
178 }
179 else if (pixfmt == RDP_PIXFMT_YV12 && xf_tsmf_is_format_supported(xv, RDP_PIXFMT_I420))
180 {
181 xvpixfmt = RDP_PIXFMT_I420;
182 converti420yv12 = TRUE;
183 }
184 else
185 {
186 WLog_DBG(TAG, "pixel format 0x%" PRIX32 " not supported by hardware.", pixfmt);
187 free(xrects);
188 return -1003;
189 }
190
191 image = XvShmCreateImage(xfc->display, xv->xv_port, xvpixfmt, 0, event->frameWidth,
192 event->frameHeight, &shminfo);
193
194 if (xv->xv_image_size != image->data_size)
195 {
196 if (xv->xv_image_size > 0)
197 {
198 shmdt(xv->xv_shmaddr);
199 shmctl(xv->xv_shmid, IPC_RMID, NULL);
200 }
201
202 xv->xv_image_size = image->data_size;
203 xv->xv_shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777);
204 xv->xv_shmaddr = shmat(xv->xv_shmid, 0, 0);
205 }
206
207 shminfo.shmid = xv->xv_shmid;
208 shminfo.shmaddr = image->data = xv->xv_shmaddr;
209 shminfo.readOnly = FALSE;
210
211 if (!XShmAttach(xfc->display, &shminfo))
212 {
213 XFree(image);
214 free(xrects);
215 WLog_DBG(TAG, "XShmAttach failed.");
216 return -1004;
217 }
218
219 /* The video driver may align each line to a different size
220 and we need to convert our original image data. */
221 switch (pixfmt)
222 {
223 case RDP_PIXFMT_I420:
224 case RDP_PIXFMT_YV12:
225 /* Y */
226 if (image->pitches[0] == event->frameWidth)
227 {
228 CopyMemory(image->data + image->offsets[0], event->frameData,
229 1ULL * event->frameWidth * event->frameHeight);
230 }
231 else
232 {
233 for (int i = 0; i < event->frameHeight; i++)
234 {
235 CopyMemory(image->data + 1ULL * image->offsets[0] +
236 1ULL * i * image->pitches[0],
237 event->frameData + 1ULL * i * event->frameWidth, event->frameWidth);
238 }
239 }
240 /* UV */
241 /* Conversion between I420 and YV12 is to simply swap U and V */
242 if (!converti420yv12)
243 {
244 data1 = event->frameData + 1ULL * event->frameWidth * event->frameHeight;
245 data2 = event->frameData + 1ULL * event->frameWidth * event->frameHeight +
246 1ULL * event->frameWidth * event->frameHeight / 4;
247 }
248 else
249 {
250 data2 = event->frameData + 1ULL * event->frameWidth * event->frameHeight;
251 data1 = event->frameData + 1ULL * event->frameWidth * event->frameHeight +
252 1ULL * event->frameWidth * event->frameHeight / 4;
253 image->id = pixfmt == RDP_PIXFMT_I420 ? RDP_PIXFMT_YV12 : RDP_PIXFMT_I420;
254 }
255
256 if (image->pitches[1] * 2 == event->frameWidth)
257 {
258 CopyMemory(image->data + image->offsets[1], data1,
259 event->frameWidth * event->frameHeight / 4);
260 CopyMemory(image->data + image->offsets[2], data2,
261 event->frameWidth * event->frameHeight / 4);
262 }
263 else
264 {
265 for (int i = 0; i < event->frameHeight / 2; i++)
266 {
267 CopyMemory(image->data + 1ULL * image->offsets[1] +
268 1ULL * i * image->pitches[1],
269 data1 + 1ULL * i * event->frameWidth / 2, event->frameWidth / 2);
270 CopyMemory(image->data + 1ULL * image->offsets[2] +
271 1ULL * i * image->pitches[2],
272 data2 + 1ULL * i * event->frameWidth / 2, event->frameWidth / 2);
273 }
274 }
275 break;
276
277 default:
278 if (image->data_size < 0)
279 {
280 free(xrects);
281 return -2000;
282 }
283 else
284 {
285 const size_t size = ((UINT32)image->data_size <= event->frameSize)
286 ? (UINT32)image->data_size
287 : event->frameSize;
288 CopyMemory(image->data, event->frameData, size);
289 }
290 break;
291 }
292
293 XvShmPutImage(xfc->display, xv->xv_port, xfc->window->handle, xfc->gc, image, 0, 0,
294 image->width, image->height, event->x, event->y, event->width, event->height,
295 FALSE);
296
297 if (xv->xv_colorkey_atom == None)
298 XSetClipMask(xfc->display, xfc->gc, None);
299
300 XSync(xfc->display, FALSE);
301
302 XShmDetach(xfc->display, &shminfo);
303 XFree(image);
304
305 free(xrects);
306
307 return 1;
308}
309
310static int xf_tsmf_xv_init(xfContext* xfc, TsmfClientContext* tsmf)
311{
312 int ret = 0;
313 unsigned int version = 0;
314 unsigned int release = 0;
315 unsigned int event_base = 0;
316 unsigned int error_base = 0;
317 unsigned int request_base = 0;
318 unsigned int num_adaptors = 0;
319 xfXvContext* xv = NULL;
320 XvAdaptorInfo* ai = NULL;
321 XvAttribute* attr = NULL;
322 XvImageFormatValues* fo = NULL;
323
324 if (xfc->xv_context)
325 return 1; /* context already created */
326
327 xv = (xfXvContext*)calloc(1, sizeof(xfXvContext));
328
329 if (!xv)
330 return -1;
331
332 xfc->xv_context = xv;
333
334 xv->xv_colorkey_atom = None;
335 xv->xv_image_size = 0;
336 xv->xv_port = xv_port;
337
338 if (!XShmQueryExtension(xfc->display))
339 {
340 WLog_DBG(TAG, "no xshm available.");
341 return -1;
342 }
343
344 ret =
345 XvQueryExtension(xfc->display, &version, &release, &request_base, &event_base, &error_base);
346
347 if (ret != Success)
348 {
349 WLog_DBG(TAG, "XvQueryExtension failed %d.", ret);
350 return -1;
351 }
352
353 WLog_DBG(TAG, "version %u release %u", version, release);
354
355 ret = XvQueryAdaptors(xfc->display, DefaultRootWindow(xfc->display), &num_adaptors, &ai);
356
357 if (ret != Success)
358 {
359 WLog_DBG(TAG, "XvQueryAdaptors failed %d.", ret);
360 return -1;
361 }
362
363 for (unsigned int i = 0; i < num_adaptors; i++)
364 {
365 WLog_DBG(TAG, "adapter port %lu-%lu (%s)", ai[i].base_id,
366 ai[i].base_id + ai[i].num_ports - 1, ai[i].name);
367
368 if (xv->xv_port == 0 && i == num_adaptors - 1)
369 xv->xv_port = ai[i].base_id;
370 }
371
372 if (num_adaptors > 0)
373 XvFreeAdaptorInfo(ai);
374
375 if (xv->xv_port == 0)
376 {
377 WLog_DBG(TAG, "no adapter selected, video frames will not be processed.");
378 return -1;
379 }
380 WLog_DBG(TAG, "selected %ld", xv->xv_port);
381
382 attr = XvQueryPortAttributes(xfc->display, xv->xv_port, &ret);
383
384 unsigned int i = 0;
385 for (; i < (unsigned int)ret; i++)
386 {
387 if (strcmp(attr[i].name, "XV_COLORKEY") == 0)
388 {
389 static wLog* log = NULL;
390 if (!log)
391 log = WLog_Get(TAG);
392 xv->xv_colorkey_atom = Logging_XInternAtom(log, xfc->display, "XV_COLORKEY", FALSE);
393 XvSetPortAttribute(xfc->display, xv->xv_port, xv->xv_colorkey_atom,
394 attr[i].min_value + 1);
395 break;
396 }
397 }
398 XFree(attr);
399
400 WLog_DBG(TAG, "xf_tsmf_init: pixel format ");
401
402 fo = XvListImageFormats(xfc->display, xv->xv_port, &ret);
403
404 if (ret > 0)
405 {
406 xv->xv_pixfmts = (UINT32*)calloc((ret + 1), sizeof(UINT32));
407
408 size_t x = 0;
409 for (; x < (size_t)ret; x++)
410 {
411 xv->xv_pixfmts[x] = fo[x].id;
412 WLog_DBG(TAG, "%c%c%c%c ", ((char*)(xv->xv_pixfmts + x))[0],
413 ((char*)(xv->xv_pixfmts + x))[1], ((char*)(xv->xv_pixfmts + x))[2],
414 ((char*)(xv->xv_pixfmts + x))[3]);
415 }
416 xv->xv_pixfmts[x] = 0;
417 }
418 XFree(fo);
419
420 if (tsmf)
421 {
422 xfc->tsmf = tsmf;
423 tsmf->custom = (void*)xfc;
424
425 tsmf->FrameEvent = xf_tsmf_xv_video_frame_event;
426 }
427
428 return 1;
429}
430
431static int xf_tsmf_xv_uninit(xfContext* xfc, TsmfClientContext* tsmf)
432{
433 xfXvContext* xv = (xfXvContext*)xfc->xv_context;
434
435 WINPR_UNUSED(tsmf);
436 if (xv)
437 {
438 if (xv->xv_image_size > 0)
439 {
440 shmdt(xv->xv_shmaddr);
441 shmctl(xv->xv_shmid, IPC_RMID, NULL);
442 }
443 if (xv->xv_pixfmts)
444 {
445 free(xv->xv_pixfmts);
446 xv->xv_pixfmts = NULL;
447 }
448 free(xv);
449 xfc->xv_context = NULL;
450 }
451
452 if (xfc->tsmf)
453 {
454 xfc->tsmf->custom = NULL;
455 xfc->tsmf = NULL;
456 }
457
458 return 1;
459}
460
461#endif
462
463int xf_tsmf_init(xfContext* xfc, TsmfClientContext* tsmf)
464{
465#ifdef WITH_XV
466 return xf_tsmf_xv_init(xfc, tsmf);
467#else
468 return 1;
469#endif
470}
471
472int xf_tsmf_uninit(xfContext* xfc, TsmfClientContext* tsmf)
473{
474#ifdef WITH_XV
475 return xf_tsmf_xv_uninit(xfc, tsmf);
476#else
477 return 1;
478#endif
479}