Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / client / X11 / xf_tsmf.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * X11 Video Redirection
4  *
5  * Copyright 2010-2011 Vic Lee
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/ipc.h>
24 #include <sys/shm.h>
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <X11/Xatom.h>
28 #include <X11/extensions/XShm.h>
29 #include <freerdp/utils/memory.h>
30 #include <freerdp/utils/event.h>
31 #include <freerdp/plugins/tsmf.h>
32
33 #include "xf_tsmf.h"
34
35 #ifdef WITH_XV
36
37 #include <X11/extensions/Xv.h>
38 #include <X11/extensions/Xvlib.h>
39
40 typedef struct xf_xv_context xfXvContext;
41
42 struct xf_xv_context
43 {
44         long xv_port;
45         Atom xv_colorkey_atom;
46         int xv_image_size;
47         int xv_shmid;
48         char* xv_shmaddr;
49         uint32* xv_pixfmts;
50 };
51
52 #ifdef WITH_DEBUG_XV
53 #define DEBUG_XV(fmt, ...) DEBUG_CLASS(XV, fmt, ## __VA_ARGS__)
54 #else
55 #define DEBUG_XV(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
56 #endif
57
58 void xf_tsmf_init(xfInfo* xfi, long xv_port)
59 {
60         int ret;
61         unsigned int i;
62         unsigned int version;
63         unsigned int release;
64         unsigned int event_base;
65         unsigned int error_base;
66         unsigned int request_base;
67         unsigned int num_adaptors;
68         xfXvContext* xv;
69         XvAdaptorInfo* ai;
70         XvAttribute* attr;
71         XvImageFormatValues* fo;
72
73         xv = xnew(xfXvContext);
74         xfi->xv_context = xv;
75
76         xv->xv_colorkey_atom = None;
77         xv->xv_image_size = 0;
78         xv->xv_port = xv_port;
79
80         if (!XShmQueryExtension(xfi->display))
81         {
82                 DEBUG_XV("no shmem available.");
83                 return;
84         }
85
86         ret = XvQueryExtension(xfi->display, &version, &release, &request_base, &event_base, &error_base);
87         if (ret != Success)
88         {
89                 DEBUG_XV("XvQueryExtension failed %d.", ret);
90                 return;
91         }
92         DEBUG_XV("version %u release %u", version, release);
93
94         ret = XvQueryAdaptors(xfi->display, DefaultRootWindow(xfi->display),
95                 &num_adaptors, &ai);
96         if (ret != Success)
97         {
98                 DEBUG_XV("XvQueryAdaptors failed %d.", ret);
99                 return;
100         }
101
102         for (i = 0; i < num_adaptors; i++)
103         {
104                 DEBUG_XV("adapter port %ld-%ld (%s)", ai[i].base_id,
105                         ai[i].base_id + ai[i].num_ports - 1, ai[i].name);
106                 if (xv->xv_port == 0 && i == num_adaptors - 1)
107                         xv->xv_port = ai[i].base_id;
108         }
109
110         if (num_adaptors > 0)
111                 XvFreeAdaptorInfo(ai);
112
113         if (xv->xv_port == 0)
114         {
115                 DEBUG_XV("no adapter selected, video frames will not be processed.");
116                 return;
117         }
118         DEBUG_XV("selected %ld", xv->xv_port);
119
120         attr = XvQueryPortAttributes(xfi->display, xv->xv_port, &ret);
121         for (i = 0; i < (unsigned int)ret; i++)
122         {
123                 if (strcmp(attr[i].name, "XV_COLORKEY") == 0)
124                 {
125                         xv->xv_colorkey_atom = XInternAtom(xfi->display, "XV_COLORKEY", false);
126                         XvSetPortAttribute(xfi->display, xv->xv_port, xv->xv_colorkey_atom, attr[i].min_value + 1);
127                         break;
128                 }
129         }
130         XFree(attr);
131
132 #ifdef WITH_DEBUG_XV
133         printf("xf_tsmf_init: pixel format ");
134 #endif
135         fo = XvListImageFormats(xfi->display, xv->xv_port, &ret);
136         if (ret > 0)
137         {
138                 xv->xv_pixfmts = (uint32*) xzalloc((ret + 1) * sizeof(uint32));
139                 for (i = 0; i < ret; i++)
140                 {
141                         xv->xv_pixfmts[i] = fo[i].id;
142 #ifdef WITH_DEBUG_XV
143                         printf("%c%c%c%c ", ((char*)(xv->xv_pixfmts + i))[0], ((char*)(xv->xv_pixfmts + i))[1],
144                                 ((char*)(xv->xv_pixfmts + i))[2], ((char*)(xv->xv_pixfmts + i))[3]);
145 #endif
146                 }
147                 xv->xv_pixfmts[i] = 0;
148         }
149         XFree(fo);
150 #ifdef WITH_DEBUG_XV
151         printf("\n");
152 #endif
153 }
154
155 void xf_tsmf_uninit(xfInfo* xfi)
156 {
157         xfXvContext* xv = (xfXvContext*) xfi->xv_context;
158
159         if (xv)
160         {
161                 if (xv->xv_image_size > 0)
162                 {
163                         shmdt(xv->xv_shmaddr);
164                         shmctl(xv->xv_shmid, IPC_RMID, NULL);
165                 }
166                 if (xv->xv_pixfmts)
167                 {
168                         xfree(xv->xv_pixfmts);
169                         xv->xv_pixfmts = NULL;
170                 }
171                 xfree(xv);
172                 xfi->xv_context = NULL;
173         }
174 }
175
176 static boolean
177 xf_tsmf_is_format_supported(xfXvContext* xv, uint32 pixfmt)
178 {
179         int i;
180
181         if (!xv->xv_pixfmts)
182                 return false;
183
184         for (i = 0; xv->xv_pixfmts[i]; i++)
185         {
186                 if (xv->xv_pixfmts[i] == pixfmt)
187                         return true;
188         }
189
190         return false;
191 }
192
193 static void xf_process_tsmf_video_frame_event(xfInfo* xfi, RDP_VIDEO_FRAME_EVENT* vevent)
194 {
195         int i;
196         uint8* data1;
197         uint8* data2;
198         uint32 pixfmt;
199         XvImage * image;
200         int colorkey = 0;
201         XShmSegmentInfo shminfo;
202         xfXvContext* xv = (xfXvContext*) xfi->xv_context;
203
204         if (xv->xv_port == 0)
205                 return;
206
207         /* In case the player is minimized */
208         if (vevent->x < -2048 || vevent->y < -2048 || vevent->num_visible_rects <= 0)
209                 return;
210
211         if (xv->xv_colorkey_atom != None)
212         {
213                 XvGetPortAttribute(xfi->display, xv->xv_port, xv->xv_colorkey_atom, &colorkey);
214                 XSetFunction(xfi->display, xfi->gc, GXcopy);
215                 XSetFillStyle(xfi->display, xfi->gc, FillSolid);
216                 XSetForeground(xfi->display, xfi->gc, colorkey);
217                 for (i = 0; i < vevent->num_visible_rects; i++)
218                 {
219                         XFillRectangle(xfi->display, xfi->window->handle, xfi->gc,
220                                 vevent->x + vevent->visible_rects[i].x,
221                                 vevent->y + vevent->visible_rects[i].y,
222                                 vevent->visible_rects[i].width,
223                                 vevent->visible_rects[i].height);
224                 }
225         }
226
227         pixfmt = vevent->frame_pixfmt;
228         image = XvShmCreateImage(xfi->display, xv->xv_port,
229                 pixfmt, 0, vevent->frame_width, vevent->frame_height, &shminfo);
230         if (xv->xv_image_size != image->data_size)
231         {
232                 if (xv->xv_image_size > 0)
233                 {
234                         shmdt(xv->xv_shmaddr);
235                         shmctl(xv->xv_shmid, IPC_RMID, NULL);
236                 }
237                 xv->xv_image_size = image->data_size;
238                 xv->xv_shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777);
239                 xv->xv_shmaddr = shmat(xv->xv_shmid, 0, 0);
240         }
241         shminfo.shmid = xv->xv_shmid;
242         shminfo.shmaddr = image->data = xv->xv_shmaddr;
243         shminfo.readOnly = false;
244
245         if (!XShmAttach(xfi->display, &shminfo))
246         {
247                 XFree(image);
248                 DEBUG_XV("XShmAttach failed.");
249                 return;
250         }
251
252         /* The video driver may align each line to a different size
253            and we need to convert our original image data. */
254         switch (pixfmt)
255         {
256                 case RDP_PIXFMT_I420:
257                 case RDP_PIXFMT_YV12:
258                         if (!xf_tsmf_is_format_supported(xv, RDP_PIXFMT_I420) &&
259                                 !xf_tsmf_is_format_supported(xv, RDP_PIXFMT_YV12))
260                         {
261                                 DEBUG_XV("pixel format 0x%X not supported by hardware.", pixfmt);
262                                 break;
263                         }
264                         /* Y */
265                         if (image->pitches[0] == vevent->frame_width)
266                         {
267                                 memcpy(image->data + image->offsets[0],
268                                         vevent->frame_data,
269                                         vevent->frame_width * vevent->frame_height);
270                         }
271                         else
272                         {
273                                 for (i = 0; i < vevent->frame_height; i++)
274                                 {
275                                         memcpy(image->data + image->offsets[0] + i * image->pitches[0],
276                                                 vevent->frame_data + i * vevent->frame_width,
277                                                 vevent->frame_width);
278                                 }
279                         }
280                         /* UV */
281                         /* Conversion between I420 and YV12 is to simply swap U and V */
282                         if (xf_tsmf_is_format_supported(xv, pixfmt))
283                         {
284                                 data1 = vevent->frame_data + vevent->frame_width * vevent->frame_height;
285                                 data2 = vevent->frame_data + vevent->frame_width * vevent->frame_height +
286                                         vevent->frame_width * vevent->frame_height / 4;
287                         }
288                         else
289                         {
290                                 data2 = vevent->frame_data + vevent->frame_width * vevent->frame_height;
291                                 data1 = vevent->frame_data + vevent->frame_width * vevent->frame_height +
292                                         vevent->frame_width * vevent->frame_height / 4;
293                                 image->id = pixfmt == RDP_PIXFMT_I420 ? RDP_PIXFMT_YV12 : RDP_PIXFMT_I420;
294                         }
295                         if (image->pitches[1] * 2 == vevent->frame_width)
296                         {
297                                 memcpy(image->data + image->offsets[1],
298                                         data1,
299                                         vevent->frame_width * vevent->frame_height / 4);
300                                 memcpy(image->data + image->offsets[2],
301                                         data2,
302                                         vevent->frame_width * vevent->frame_height / 4);
303                         }
304                         else
305                         {
306                                 for (i = 0; i < vevent->frame_height / 2; i++)
307                                 {
308                                         memcpy(image->data + image->offsets[1] + i * image->pitches[1],
309                                                 data1 + i * vevent->frame_width / 2,
310                                                 vevent->frame_width / 2);
311                                         memcpy(image->data + image->offsets[2] + i * image->pitches[2],
312                                                 data2 + i * vevent->frame_width / 2,
313                                                 vevent->frame_width / 2);
314                                 }
315                         }
316                         break;
317
318                 default:
319                         memcpy(image->data, vevent->frame_data, image->data_size <= vevent->frame_size ?
320                                 image->data_size : vevent->frame_size);
321                         break;
322         }
323
324         XvShmPutImage(xfi->display, xv->xv_port, xfi->window->handle, xfi->gc, image,
325                 0, 0, image->width, image->height,
326                 vevent->x, vevent->y, vevent->width, vevent->height, false);
327         XSync(xfi->display, false);
328
329         XShmDetach(xfi->display, &shminfo);
330         XFree(image);
331 }
332
333 static void xf_process_tsmf_redraw_event(xfInfo* xfi, RDP_REDRAW_EVENT* revent)
334 {
335         XSetFunction(xfi->display, xfi->gc, GXcopy);
336         XSetFillStyle(xfi->display, xfi->gc, FillSolid);
337         XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc,
338                 revent->x, revent->y, revent->width, revent->height, revent->x, revent->y);
339 }
340
341 void xf_process_tsmf_event(xfInfo* xfi, RDP_EVENT* event)
342 {
343         switch (event->event_type)
344         {
345                 case RDP_EVENT_TYPE_TSMF_VIDEO_FRAME:
346                         xf_process_tsmf_video_frame_event(xfi, (RDP_VIDEO_FRAME_EVENT*) event);
347                         break;
348
349                 case RDP_EVENT_TYPE_TSMF_REDRAW:
350                         xf_process_tsmf_redraw_event(xfi, (RDP_REDRAW_EVENT*) event);
351                         break;
352
353         }
354 }
355
356 #else /* WITH_XV */
357
358 void xf_tsmf_init(xfInfo* xfi, long xv_port)
359 {
360 }
361
362 void xf_tsmf_uninit(xfInfo* xfi)
363 {
364 }
365
366 void xf_process_tsmf_event(xfInfo* xfi, RDP_EVENT* event)
367 {
368 }
369
370 #endif /* WITH_XV */