2 * FreeRDP: A Remote Desktop Protocol Client
3 * X11 Video Redirection
5 * Copyright 2010-2011 Vic Lee
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
37 #include <X11/extensions/Xv.h>
38 #include <X11/extensions/Xvlib.h>
40 typedef struct xf_xv_context xfXvContext;
45 Atom xv_colorkey_atom;
53 #define DEBUG_XV(fmt, ...) DEBUG_CLASS(XV, fmt, ## __VA_ARGS__)
55 #define DEBUG_XV(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
58 void xf_tsmf_init(xfInfo* xfi, long xv_port)
64 unsigned int event_base;
65 unsigned int error_base;
66 unsigned int request_base;
67 unsigned int num_adaptors;
71 XvImageFormatValues* fo;
73 xv = xnew(xfXvContext);
76 xv->xv_colorkey_atom = None;
77 xv->xv_image_size = 0;
78 xv->xv_port = xv_port;
80 if (!XShmQueryExtension(xfi->display))
82 DEBUG_XV("no shmem available.");
86 ret = XvQueryExtension(xfi->display, &version, &release, &request_base, &event_base, &error_base);
89 DEBUG_XV("XvQueryExtension failed %d.", ret);
92 DEBUG_XV("version %u release %u", version, release);
94 ret = XvQueryAdaptors(xfi->display, DefaultRootWindow(xfi->display),
98 DEBUG_XV("XvQueryAdaptors failed %d.", ret);
102 for (i = 0; i < num_adaptors; i++)
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;
110 if (num_adaptors > 0)
111 XvFreeAdaptorInfo(ai);
113 if (xv->xv_port == 0)
115 DEBUG_XV("no adapter selected, video frames will not be processed.");
118 DEBUG_XV("selected %ld", xv->xv_port);
120 attr = XvQueryPortAttributes(xfi->display, xv->xv_port, &ret);
121 for (i = 0; i < (unsigned int)ret; i++)
123 if (strcmp(attr[i].name, "XV_COLORKEY") == 0)
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);
133 printf("xf_tsmf_init: pixel format ");
135 fo = XvListImageFormats(xfi->display, xv->xv_port, &ret);
138 xv->xv_pixfmts = (uint32*) xzalloc((ret + 1) * sizeof(uint32));
139 for (i = 0; i < ret; i++)
141 xv->xv_pixfmts[i] = fo[i].id;
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]);
147 xv->xv_pixfmts[i] = 0;
155 void xf_tsmf_uninit(xfInfo* xfi)
157 xfXvContext* xv = (xfXvContext*) xfi->xv_context;
161 if (xv->xv_image_size > 0)
163 shmdt(xv->xv_shmaddr);
164 shmctl(xv->xv_shmid, IPC_RMID, NULL);
168 xfree(xv->xv_pixfmts);
169 xv->xv_pixfmts = NULL;
172 xfi->xv_context = NULL;
177 xf_tsmf_is_format_supported(xfXvContext* xv, uint32 pixfmt)
184 for (i = 0; xv->xv_pixfmts[i]; i++)
186 if (xv->xv_pixfmts[i] == pixfmt)
193 static void xf_process_tsmf_video_frame_event(xfInfo* xfi, RDP_VIDEO_FRAME_EVENT* vevent)
201 XShmSegmentInfo shminfo;
202 xfXvContext* xv = (xfXvContext*) xfi->xv_context;
204 if (xv->xv_port == 0)
207 /* In case the player is minimized */
208 if (vevent->x < -2048 || vevent->y < -2048 || vevent->num_visible_rects <= 0)
211 if (xv->xv_colorkey_atom != None)
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++)
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);
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)
232 if (xv->xv_image_size > 0)
234 shmdt(xv->xv_shmaddr);
235 shmctl(xv->xv_shmid, IPC_RMID, NULL);
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);
241 shminfo.shmid = xv->xv_shmid;
242 shminfo.shmaddr = image->data = xv->xv_shmaddr;
243 shminfo.readOnly = false;
245 if (!XShmAttach(xfi->display, &shminfo))
248 DEBUG_XV("XShmAttach failed.");
252 /* The video driver may align each line to a different size
253 and we need to convert our original image data. */
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))
261 DEBUG_XV("pixel format 0x%X not supported by hardware.", pixfmt);
265 if (image->pitches[0] == vevent->frame_width)
267 memcpy(image->data + image->offsets[0],
269 vevent->frame_width * vevent->frame_height);
273 for (i = 0; i < vevent->frame_height; i++)
275 memcpy(image->data + image->offsets[0] + i * image->pitches[0],
276 vevent->frame_data + i * vevent->frame_width,
277 vevent->frame_width);
281 /* Conversion between I420 and YV12 is to simply swap U and V */
282 if (xf_tsmf_is_format_supported(xv, pixfmt))
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;
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;
295 if (image->pitches[1] * 2 == vevent->frame_width)
297 memcpy(image->data + image->offsets[1],
299 vevent->frame_width * vevent->frame_height / 4);
300 memcpy(image->data + image->offsets[2],
302 vevent->frame_width * vevent->frame_height / 4);
306 for (i = 0; i < vevent->frame_height / 2; i++)
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);
319 memcpy(image->data, vevent->frame_data, image->data_size <= vevent->frame_size ?
320 image->data_size : vevent->frame_size);
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);
329 XShmDetach(xfi->display, &shminfo);
333 static void xf_process_tsmf_redraw_event(xfInfo* xfi, RDP_REDRAW_EVENT* revent)
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);
341 void xf_process_tsmf_event(xfInfo* xfi, RDP_EVENT* event)
343 switch (event->event_type)
345 case RDP_EVENT_TYPE_TSMF_VIDEO_FRAME:
346 xf_process_tsmf_video_frame_event(xfi, (RDP_VIDEO_FRAME_EVENT*) event);
349 case RDP_EVENT_TYPE_TSMF_REDRAW:
350 xf_process_tsmf_redraw_event(xfi, (RDP_REDRAW_EVENT*) event);
358 void xf_tsmf_init(xfInfo* xfi, long xv_port)
362 void xf_tsmf_uninit(xfInfo* xfi)
366 void xf_process_tsmf_event(xfInfo* xfi, RDP_EVENT* event)