FreeRDP
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 
43 static long xv_port = 0;
44 
45 struct 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 };
54 typedef struct xf_xv_context xfXvContext;
55 
56 #define TAG CLIENT_TAG("x11")
57 
58 static 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 
72 static 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 
310 static 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 
431 static 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 
463 int 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 
472 int 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 }