1 | /* GStreamer |
---|
2 | * Copyright (C) <2003> Julien Moutte <julien@moutte.net> |
---|
3 | * |
---|
4 | * This library is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU Library General Public |
---|
6 | * License as published by the Free Software Foundation; either |
---|
7 | * version 2 of the License, or (at your option) any later version. |
---|
8 | * |
---|
9 | * This library is distributed in the hope that it will be useful, |
---|
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * Library General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU Library General Public |
---|
15 | * License along with this library; if not, write to the |
---|
16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
17 | * Boston, MA 02111-1307, USA. |
---|
18 | */ |
---|
19 | |
---|
20 | #ifdef HAVE_CONFIG_H |
---|
21 | #include "config.h" |
---|
22 | #endif |
---|
23 | |
---|
24 | /* Our interfaces */ |
---|
25 | #include <gst/navigation/navigation.h> |
---|
26 | #include <gst/xoverlay/xoverlay.h> |
---|
27 | |
---|
28 | /* Object header */ |
---|
29 | #include "ximagesink.h" |
---|
30 | |
---|
31 | /* Debugging category */ |
---|
32 | #include <gst/gstinfo.h> |
---|
33 | GST_DEBUG_CATEGORY_STATIC (gst_debug_ximagesink); |
---|
34 | #define GST_CAT_DEFAULT gst_debug_ximagesink |
---|
35 | |
---|
36 | typedef struct |
---|
37 | { |
---|
38 | unsigned long flags; |
---|
39 | unsigned long functions; |
---|
40 | unsigned long decorations; |
---|
41 | long input_mode; |
---|
42 | unsigned long status; |
---|
43 | } |
---|
44 | MotifWmHints, MwmHints; |
---|
45 | |
---|
46 | #define MWM_HINTS_DECORATIONS (1L << 1) |
---|
47 | |
---|
48 | static void gst_ximagesink_buffer_free (GstBuffer * buffer); |
---|
49 | static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink, |
---|
50 | GstXImage * ximage); |
---|
51 | |
---|
52 | /* ElementFactory information */ |
---|
53 | static GstElementDetails gst_ximagesink_details = |
---|
54 | GST_ELEMENT_DETAILS ("Video sink", |
---|
55 | "Sink/Video", |
---|
56 | "A standard X based videosink", |
---|
57 | "Julien Moutte <julien@moutte.net>"); |
---|
58 | |
---|
59 | /* Default template - initiated with class struct to allow gst-register to work |
---|
60 | without X running */ |
---|
61 | static GstStaticPadTemplate gst_ximagesink_sink_template_factory = |
---|
62 | GST_STATIC_PAD_TEMPLATE ("sink", |
---|
63 | GST_PAD_SINK, |
---|
64 | GST_PAD_ALWAYS, |
---|
65 | GST_STATIC_CAPS ("video/x-raw-rgb, " |
---|
66 | "framerate = (double) [ 1.0, 100.0 ], " |
---|
67 | "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") |
---|
68 | ); |
---|
69 | |
---|
70 | enum |
---|
71 | { |
---|
72 | ARG_0, |
---|
73 | ARG_DISPLAY, |
---|
74 | ARG_SYNCHRONOUS, |
---|
75 | ARG_PIXEL_ASPECT_RATIO |
---|
76 | /* FILL ME */ |
---|
77 | }; |
---|
78 | |
---|
79 | static GstVideoSinkClass *parent_class = NULL; |
---|
80 | static gboolean error_caught = FALSE; |
---|
81 | |
---|
82 | /* ============================================================= */ |
---|
83 | /* */ |
---|
84 | /* Private Methods */ |
---|
85 | /* */ |
---|
86 | /* ============================================================= */ |
---|
87 | |
---|
88 | /* X11 stuff */ |
---|
89 | |
---|
90 | static int |
---|
91 | gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent) |
---|
92 | { |
---|
93 | char error_msg[1024]; |
---|
94 | |
---|
95 | XGetErrorText (display, xevent->error_code, error_msg, 1024); |
---|
96 | GST_DEBUG ("ximagesink failed to use XShm calls. error: %s", error_msg); |
---|
97 | error_caught = TRUE; |
---|
98 | return 0; |
---|
99 | } |
---|
100 | |
---|
101 | /* This function checks that it is actually really possible to create an image |
---|
102 | using XShm */ |
---|
103 | static gboolean |
---|
104 | gst_ximagesink_check_xshm_calls (GstXContext * xcontext) |
---|
105 | { |
---|
106 | #ifndef HAVE_XSHM |
---|
107 | return FALSE; |
---|
108 | #else |
---|
109 | GstXImage *ximage = NULL; |
---|
110 | int (*handler) (Display *, XErrorEvent *); |
---|
111 | gboolean result = FALSE; |
---|
112 | |
---|
113 | g_return_val_if_fail (xcontext != NULL, FALSE); |
---|
114 | |
---|
115 | ximage = g_new0 (GstXImage, 1); |
---|
116 | g_return_val_if_fail (ximage != NULL, FALSE); |
---|
117 | |
---|
118 | /* Setting an error handler to catch failure */ |
---|
119 | error_caught = FALSE; |
---|
120 | handler = XSetErrorHandler (gst_ximagesink_handle_xerror); |
---|
121 | |
---|
122 | /* Trying to create a 1x1 picture */ |
---|
123 | GST_DEBUG ("XShmCreateImage of 1x1"); |
---|
124 | |
---|
125 | ximage->ximage = XShmCreateImage (xcontext->disp, xcontext->visual, |
---|
126 | xcontext->depth, ZPixmap, NULL, &ximage->SHMInfo, 1, 1); |
---|
127 | if (!ximage->ximage) { |
---|
128 | GST_WARNING ("could not XShmCreateImage a 1x1 image"); |
---|
129 | goto beach; |
---|
130 | } |
---|
131 | ximage->size = ximage->ximage->height * ximage->ximage->bytes_per_line; |
---|
132 | |
---|
133 | ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size, IPC_CREAT | 0777); |
---|
134 | if (ximage->SHMInfo.shmid == -1) { |
---|
135 | GST_WARNING ("could not get shared memory of %d bytes", ximage->size); |
---|
136 | goto beach; |
---|
137 | } |
---|
138 | |
---|
139 | ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0); |
---|
140 | if (ximage->SHMInfo.shmaddr == ((void *) -1)) { |
---|
141 | GST_WARNING ("Failed to shmat: %s", g_strerror (errno)); |
---|
142 | goto beach; |
---|
143 | } |
---|
144 | |
---|
145 | ximage->ximage->data = ximage->SHMInfo.shmaddr; |
---|
146 | ximage->SHMInfo.readOnly = FALSE; |
---|
147 | |
---|
148 | if (XShmAttach (xcontext->disp, &ximage->SHMInfo) == 0) { |
---|
149 | GST_WARNING ("Failed to XShmAttach"); |
---|
150 | goto beach; |
---|
151 | } |
---|
152 | |
---|
153 | XSync (xcontext->disp, 0); |
---|
154 | |
---|
155 | XShmDetach (xcontext->disp, &ximage->SHMInfo); |
---|
156 | XSync (xcontext->disp, FALSE); |
---|
157 | |
---|
158 | shmdt (ximage->SHMInfo.shmaddr); |
---|
159 | shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0); |
---|
160 | |
---|
161 | /* To be sure, reset the SHMInfo entry */ |
---|
162 | ximage->SHMInfo.shmaddr = ((void *) -1); |
---|
163 | |
---|
164 | /* store whether we succeeded in result and reset error_caught */ |
---|
165 | result = !error_caught; |
---|
166 | error_caught = FALSE; |
---|
167 | |
---|
168 | beach: |
---|
169 | XSetErrorHandler (handler); |
---|
170 | if (ximage->ximage) |
---|
171 | XFree (ximage->ximage); |
---|
172 | g_free (ximage); |
---|
173 | XSync (xcontext->disp, FALSE); |
---|
174 | return result; |
---|
175 | #endif /* HAVE_XSHM */ |
---|
176 | } |
---|
177 | |
---|
178 | /* This function handles GstXImage creation depending on XShm availability */ |
---|
179 | static GstXImage * |
---|
180 | gst_ximagesink_ximage_new (GstXImageSink * ximagesink, gint width, gint height) |
---|
181 | { |
---|
182 | GstXImage *ximage = NULL; |
---|
183 | gboolean succeeded = FALSE; |
---|
184 | |
---|
185 | g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL); |
---|
186 | GST_DEBUG_OBJECT (ximagesink, "creating %dx%d", width, height); |
---|
187 | |
---|
188 | ximage = g_new0 (GstXImage, 1); |
---|
189 | |
---|
190 | ximage->width = width; |
---|
191 | ximage->height = height; |
---|
192 | ximage->ximagesink = ximagesink; |
---|
193 | |
---|
194 | g_mutex_lock (ximagesink->x_lock); |
---|
195 | |
---|
196 | #ifdef HAVE_XSHM |
---|
197 | if (ximagesink->xcontext->use_xshm) { |
---|
198 | ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp, |
---|
199 | ximagesink->xcontext->visual, |
---|
200 | ximagesink->xcontext->depth, |
---|
201 | ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height); |
---|
202 | if (!ximage->ximage) { |
---|
203 | GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE, (NULL), |
---|
204 | ("could not XShmCreateImage a %dx%d image")); |
---|
205 | goto beach; |
---|
206 | } |
---|
207 | |
---|
208 | /* we have to use the returned bytes_per_line for our shm size */ |
---|
209 | ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height; |
---|
210 | GST_DEBUG_OBJECT (ximagesink, "XShm image size is %d, width %d, stride %d", |
---|
211 | ximage->size, ximage->width, ximage->ximage->bytes_per_line); |
---|
212 | |
---|
213 | ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size, |
---|
214 | IPC_CREAT | 0777); |
---|
215 | if (ximage->SHMInfo.shmid == -1) { |
---|
216 | GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE, (NULL), |
---|
217 | ("could not get shared memory of %d bytes", ximage->size)); |
---|
218 | goto beach; |
---|
219 | } |
---|
220 | |
---|
221 | ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0); |
---|
222 | if (ximage->SHMInfo.shmaddr == ((void *) -1)) { |
---|
223 | GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE, (NULL), |
---|
224 | ("Failed to shmat: %s", g_strerror (errno))); |
---|
225 | goto beach; |
---|
226 | } |
---|
227 | |
---|
228 | ximage->ximage->data = ximage->SHMInfo.shmaddr; |
---|
229 | ximage->SHMInfo.readOnly = FALSE; |
---|
230 | |
---|
231 | if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) { |
---|
232 | GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE, (NULL), |
---|
233 | ("Failed to XShmAttach")); |
---|
234 | goto beach; |
---|
235 | } |
---|
236 | |
---|
237 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
238 | } else |
---|
239 | #endif /* HAVE_XSHM */ |
---|
240 | { |
---|
241 | ximage->ximage = XCreateImage (ximagesink->xcontext->disp, |
---|
242 | ximagesink->xcontext->visual, |
---|
243 | ximagesink->xcontext->depth, |
---|
244 | ZPixmap, 0, NULL, |
---|
245 | ximage->width, ximage->height, ximagesink->xcontext->bpp, 0); |
---|
246 | if (!ximage->ximage) { |
---|
247 | GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE, (NULL), |
---|
248 | ("could not XCreateImage a %dx%d image")); |
---|
249 | goto beach; |
---|
250 | } |
---|
251 | |
---|
252 | /* we have to use the returned bytes_per_line for our image size */ |
---|
253 | ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height; |
---|
254 | ximage->ximage->data = g_malloc (ximage->size); |
---|
255 | |
---|
256 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
257 | } |
---|
258 | succeeded = TRUE; |
---|
259 | g_mutex_unlock (ximagesink->x_lock); |
---|
260 | |
---|
261 | beach: |
---|
262 | if (!succeeded) { |
---|
263 | gst_ximagesink_ximage_destroy (ximagesink, ximage); |
---|
264 | ximage = NULL; |
---|
265 | } |
---|
266 | |
---|
267 | return ximage; |
---|
268 | } |
---|
269 | |
---|
270 | /* This function destroys a GstXImage handling XShm availability */ |
---|
271 | static void |
---|
272 | gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink, GstXImage * ximage) |
---|
273 | { |
---|
274 | g_return_if_fail (ximage != NULL); |
---|
275 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
276 | |
---|
277 | /* If the destroyed image is the current one we destroy our reference too */ |
---|
278 | if (ximagesink->cur_image == ximage) |
---|
279 | ximagesink->cur_image = NULL; |
---|
280 | |
---|
281 | g_mutex_lock (ximagesink->x_lock); |
---|
282 | |
---|
283 | #ifdef HAVE_XSHM |
---|
284 | if (ximagesink->xcontext->use_xshm) { |
---|
285 | if (ximage->SHMInfo.shmaddr != ((void *) -1)) { |
---|
286 | XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo); |
---|
287 | XSync (ximagesink->xcontext->disp, 0); |
---|
288 | shmdt (ximage->SHMInfo.shmaddr); |
---|
289 | } |
---|
290 | if (ximage->SHMInfo.shmid > 0) |
---|
291 | shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0); |
---|
292 | if (ximage->ximage) |
---|
293 | XDestroyImage (ximage->ximage); |
---|
294 | |
---|
295 | } else |
---|
296 | #endif /* HAVE_XSHM */ |
---|
297 | { |
---|
298 | if (ximage->ximage) { |
---|
299 | XDestroyImage (ximage->ximage); |
---|
300 | } |
---|
301 | } |
---|
302 | |
---|
303 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
304 | |
---|
305 | g_mutex_unlock (ximagesink->x_lock); |
---|
306 | |
---|
307 | g_free (ximage); |
---|
308 | } |
---|
309 | |
---|
310 | /* This function puts a GstXImage on a GstXImageSink's window */ |
---|
311 | static void |
---|
312 | gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImage * ximage) |
---|
313 | { |
---|
314 | gint x, y; |
---|
315 | gint w, h; |
---|
316 | |
---|
317 | g_return_if_fail (ximage != NULL); |
---|
318 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
319 | |
---|
320 | /* Store a reference to the last image we put */ |
---|
321 | if (ximagesink->cur_image != ximage) |
---|
322 | ximagesink->cur_image = ximage; |
---|
323 | |
---|
324 | /* We center the image in the window; so calculate top left corner location */ |
---|
325 | x = MAX (0, (ximagesink->xwindow->width - ximage->width) / 2); |
---|
326 | y = MAX (0, (ximagesink->xwindow->height - ximage->height) / 2); |
---|
327 | |
---|
328 | w = ximage->width; |
---|
329 | h = ximage->height; |
---|
330 | |
---|
331 | g_mutex_lock (ximagesink->x_lock); |
---|
332 | #ifdef HAVE_XSHM |
---|
333 | if (ximagesink->xcontext->use_xshm) { |
---|
334 | GST_LOG_OBJECT (ximagesink, |
---|
335 | "XShmPutImage, src: %d, %d - dest: %d, %d, dim: %dx%d", |
---|
336 | 0, 0, x, y, w, h); |
---|
337 | XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win, |
---|
338 | ximagesink->xwindow->gc, ximage->ximage, 0, 0, x, y, w, h, FALSE); |
---|
339 | } else |
---|
340 | #endif /* HAVE_XSHM */ |
---|
341 | { |
---|
342 | XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win, |
---|
343 | ximagesink->xwindow->gc, ximage->ximage, 0, 0, x, y, w, h); |
---|
344 | } |
---|
345 | |
---|
346 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
347 | |
---|
348 | g_mutex_unlock (ximagesink->x_lock); |
---|
349 | } |
---|
350 | |
---|
351 | static gboolean |
---|
352 | gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink, |
---|
353 | GstXWindow * window) |
---|
354 | { |
---|
355 | Atom hints_atom = None; |
---|
356 | MotifWmHints *hints; |
---|
357 | |
---|
358 | g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE); |
---|
359 | g_return_val_if_fail (window != NULL, FALSE); |
---|
360 | |
---|
361 | g_mutex_lock (ximagesink->x_lock); |
---|
362 | |
---|
363 | hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1); |
---|
364 | if (hints_atom == None) { |
---|
365 | g_mutex_unlock (ximagesink->x_lock); |
---|
366 | return FALSE; |
---|
367 | } |
---|
368 | |
---|
369 | hints = g_malloc0 (sizeof (MotifWmHints)); |
---|
370 | |
---|
371 | hints->flags |= MWM_HINTS_DECORATIONS; |
---|
372 | hints->decorations = 1 << 0; |
---|
373 | |
---|
374 | XChangeProperty (ximagesink->xcontext->disp, window->win, |
---|
375 | hints_atom, hints_atom, 32, PropModeReplace, |
---|
376 | (guchar *) hints, sizeof (MotifWmHints) / sizeof (long)); |
---|
377 | |
---|
378 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
379 | |
---|
380 | g_mutex_unlock (ximagesink->x_lock); |
---|
381 | |
---|
382 | g_free (hints); |
---|
383 | |
---|
384 | return TRUE; |
---|
385 | } |
---|
386 | |
---|
387 | /* This function handles a GstXWindow creation */ |
---|
388 | static GstXWindow * |
---|
389 | gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height) |
---|
390 | { |
---|
391 | GstXWindow *xwindow = NULL; |
---|
392 | |
---|
393 | g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL); |
---|
394 | |
---|
395 | xwindow = g_new0 (GstXWindow, 1); |
---|
396 | |
---|
397 | xwindow->width = width; |
---|
398 | xwindow->height = height; |
---|
399 | xwindow->internal = TRUE; |
---|
400 | |
---|
401 | g_mutex_lock (ximagesink->x_lock); |
---|
402 | |
---|
403 | xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp, |
---|
404 | ximagesink->xcontext->root, |
---|
405 | 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black); |
---|
406 | |
---|
407 | XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask | |
---|
408 | StructureNotifyMask | PointerMotionMask | KeyPressMask | |
---|
409 | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask); |
---|
410 | |
---|
411 | xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL); |
---|
412 | |
---|
413 | XMapRaised (ximagesink->xcontext->disp, xwindow->win); |
---|
414 | |
---|
415 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
416 | |
---|
417 | g_mutex_unlock (ximagesink->x_lock); |
---|
418 | |
---|
419 | gst_ximagesink_xwindow_decorate (ximagesink, xwindow); |
---|
420 | |
---|
421 | gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win); |
---|
422 | |
---|
423 | return xwindow; |
---|
424 | } |
---|
425 | |
---|
426 | /* This function destroys a GstXWindow */ |
---|
427 | static void |
---|
428 | gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink, |
---|
429 | GstXWindow * xwindow) |
---|
430 | { |
---|
431 | g_return_if_fail (xwindow != NULL); |
---|
432 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
433 | |
---|
434 | g_mutex_lock (ximagesink->x_lock); |
---|
435 | |
---|
436 | /* If we did not create that window we just free the GC and let it live */ |
---|
437 | if (xwindow->internal) |
---|
438 | XDestroyWindow (ximagesink->xcontext->disp, xwindow->win); |
---|
439 | else |
---|
440 | XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0); |
---|
441 | |
---|
442 | XFreeGC (ximagesink->xcontext->disp, xwindow->gc); |
---|
443 | |
---|
444 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
445 | |
---|
446 | g_mutex_unlock (ximagesink->x_lock); |
---|
447 | |
---|
448 | g_free (xwindow); |
---|
449 | } |
---|
450 | |
---|
451 | /* This function resizes a GstXWindow */ |
---|
452 | static void |
---|
453 | gst_ximagesink_xwindow_resize (GstXImageSink * ximagesink, GstXWindow * xwindow, |
---|
454 | guint width, guint height) |
---|
455 | { |
---|
456 | g_return_if_fail (xwindow != NULL); |
---|
457 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
458 | |
---|
459 | g_mutex_lock (ximagesink->x_lock); |
---|
460 | |
---|
461 | xwindow->width = width; |
---|
462 | xwindow->height = height; |
---|
463 | |
---|
464 | XResizeWindow (ximagesink->xcontext->disp, xwindow->win, |
---|
465 | xwindow->width, xwindow->height); |
---|
466 | |
---|
467 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
468 | |
---|
469 | g_mutex_unlock (ximagesink->x_lock); |
---|
470 | } |
---|
471 | |
---|
472 | static void |
---|
473 | gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow) |
---|
474 | { |
---|
475 | g_return_if_fail (xwindow != NULL); |
---|
476 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
477 | |
---|
478 | g_mutex_lock (ximagesink->x_lock); |
---|
479 | |
---|
480 | XSetForeground (ximagesink->xcontext->disp, xwindow->gc, |
---|
481 | ximagesink->xcontext->black); |
---|
482 | |
---|
483 | XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc, |
---|
484 | 0, 0, xwindow->width, xwindow->height); |
---|
485 | |
---|
486 | XSync (ximagesink->xcontext->disp, FALSE); |
---|
487 | |
---|
488 | g_mutex_unlock (ximagesink->x_lock); |
---|
489 | } |
---|
490 | |
---|
491 | static void |
---|
492 | gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink, |
---|
493 | GstXWindow * xwindow) |
---|
494 | { |
---|
495 | XWindowAttributes attr; |
---|
496 | |
---|
497 | g_return_if_fail (xwindow != NULL); |
---|
498 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
499 | |
---|
500 | /* Update the window geometry */ |
---|
501 | g_mutex_lock (ximagesink->x_lock); |
---|
502 | XGetWindowAttributes (ximagesink->xcontext->disp, |
---|
503 | ximagesink->xwindow->win, &attr); |
---|
504 | g_mutex_unlock (ximagesink->x_lock); |
---|
505 | |
---|
506 | ximagesink->xwindow->width = attr.width; |
---|
507 | ximagesink->xwindow->height = attr.height; |
---|
508 | } |
---|
509 | |
---|
510 | static void |
---|
511 | gst_ximagesink_renegotiate_size (GstXImageSink * ximagesink) |
---|
512 | { |
---|
513 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
514 | |
---|
515 | if (!ximagesink->xwindow) |
---|
516 | return; |
---|
517 | |
---|
518 | gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow); |
---|
519 | |
---|
520 | if (ximagesink->sw_scaling_failed) |
---|
521 | return; |
---|
522 | |
---|
523 | if (ximagesink->xwindow->width <= 1 || ximagesink->xwindow->height <= 1) |
---|
524 | return; |
---|
525 | |
---|
526 | if (GST_PAD_IS_NEGOTIATING (GST_VIDEOSINK_PAD (ximagesink)) || |
---|
527 | !gst_pad_is_negotiated (GST_VIDEOSINK_PAD (ximagesink))) |
---|
528 | return; |
---|
529 | |
---|
530 | /* Window got resized or moved. We do caps negotiation again to get video |
---|
531 | scaler to fit that new size only if size of the window differs from our |
---|
532 | size. */ |
---|
533 | |
---|
534 | if (GST_VIDEOSINK_WIDTH (ximagesink) != ximagesink->xwindow->width || |
---|
535 | GST_VIDEOSINK_HEIGHT (ximagesink) != ximagesink->xwindow->height) { |
---|
536 | GstPadLinkReturn r; |
---|
537 | GstCaps *caps; |
---|
538 | |
---|
539 | caps = gst_caps_new_simple ("video/x-raw-rgb", |
---|
540 | "bpp", G_TYPE_INT, ximagesink->xcontext->bpp, |
---|
541 | "depth", G_TYPE_INT, ximagesink->xcontext->depth, |
---|
542 | "endianness", G_TYPE_INT, ximagesink->xcontext->endianness, |
---|
543 | "red_mask", G_TYPE_INT, ximagesink->xcontext->visual->red_mask, |
---|
544 | "green_mask", G_TYPE_INT, ximagesink->xcontext->visual->green_mask, |
---|
545 | "blue_mask", G_TYPE_INT, ximagesink->xcontext->visual->blue_mask, |
---|
546 | "width", G_TYPE_INT, ximagesink->xwindow->width, |
---|
547 | "height", G_TYPE_INT, ximagesink->xwindow->height, |
---|
548 | "framerate", G_TYPE_DOUBLE, ximagesink->framerate, NULL); |
---|
549 | |
---|
550 | if (ximagesink->par) { |
---|
551 | int nom, den; |
---|
552 | |
---|
553 | nom = gst_value_get_fraction_numerator (ximagesink->par); |
---|
554 | den = gst_value_get_fraction_denominator (ximagesink->par); |
---|
555 | gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, |
---|
556 | nom, den, NULL); |
---|
557 | } |
---|
558 | |
---|
559 | r = gst_pad_try_set_caps (GST_VIDEOSINK_PAD (ximagesink), caps); |
---|
560 | |
---|
561 | if ((r == GST_PAD_LINK_OK) || (r == GST_PAD_LINK_DONE)) { |
---|
562 | /* Renegotiation succeeded, we update our size and image */ |
---|
563 | GST_VIDEOSINK_WIDTH (ximagesink) = ximagesink->xwindow->width; |
---|
564 | GST_VIDEOSINK_HEIGHT (ximagesink) = ximagesink->xwindow->height; |
---|
565 | |
---|
566 | if ((ximagesink->ximage) && |
---|
567 | ((GST_VIDEOSINK_WIDTH (ximagesink) != ximagesink->ximage->width) || |
---|
568 | (GST_VIDEOSINK_HEIGHT (ximagesink) != |
---|
569 | ximagesink->ximage->height))) { |
---|
570 | /* We renew our ximage only if size changed */ |
---|
571 | GST_DEBUG_OBJECT (ximagesink, "destroying and recreating our ximage"); |
---|
572 | gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage); |
---|
573 | ximagesink->ximage = NULL; |
---|
574 | } |
---|
575 | } else { |
---|
576 | ximagesink->sw_scaling_failed = TRUE; |
---|
577 | } |
---|
578 | } |
---|
579 | } |
---|
580 | |
---|
581 | /* This function handles XEvents that might be in the queue. It generates |
---|
582 | GstEvent that will be sent upstream in the pipeline to handle interactivity |
---|
583 | and navigation. It will also listen for configure events on the window to |
---|
584 | trigger caps renegotiation so on the fly software scaling can work. */ |
---|
585 | static void |
---|
586 | gst_ximagesink_handle_xevents (GstXImageSink * ximagesink, GstPad * pad) |
---|
587 | { |
---|
588 | XEvent e; |
---|
589 | guint pointer_x = 0, pointer_y = 0; |
---|
590 | gboolean pointer_moved = FALSE; |
---|
591 | |
---|
592 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
593 | |
---|
594 | gst_ximagesink_renegotiate_size (ximagesink); |
---|
595 | |
---|
596 | /* Then we get all pointer motion events, only the last position is |
---|
597 | interesting. */ |
---|
598 | g_mutex_lock (ximagesink->x_lock); |
---|
599 | while (XCheckWindowEvent (ximagesink->xcontext->disp, |
---|
600 | ximagesink->xwindow->win, PointerMotionMask, &e)) { |
---|
601 | g_mutex_unlock (ximagesink->x_lock); |
---|
602 | |
---|
603 | switch (e.type) { |
---|
604 | case MotionNotify: |
---|
605 | pointer_x = e.xmotion.x; |
---|
606 | pointer_y = e.xmotion.y; |
---|
607 | pointer_moved = TRUE; |
---|
608 | break; |
---|
609 | default: |
---|
610 | break; |
---|
611 | } |
---|
612 | |
---|
613 | g_mutex_lock (ximagesink->x_lock); |
---|
614 | } |
---|
615 | g_mutex_unlock (ximagesink->x_lock); |
---|
616 | |
---|
617 | if (pointer_moved) { |
---|
618 | GST_DEBUG ("ximagesink pointer moved over window at %d,%d", |
---|
619 | pointer_x, pointer_y); |
---|
620 | gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink), |
---|
621 | "mouse-move", 0, pointer_x, pointer_y); |
---|
622 | } |
---|
623 | |
---|
624 | /* We get all remaining events on our window to throw them upstream */ |
---|
625 | g_mutex_lock (ximagesink->x_lock); |
---|
626 | while (XCheckWindowEvent (ximagesink->xcontext->disp, |
---|
627 | ximagesink->xwindow->win, |
---|
628 | KeyPressMask | KeyReleaseMask | |
---|
629 | ButtonPressMask | ButtonReleaseMask, &e)) { |
---|
630 | KeySym keysym; |
---|
631 | |
---|
632 | /* We lock only for the X function call */ |
---|
633 | g_mutex_unlock (ximagesink->x_lock); |
---|
634 | |
---|
635 | switch (e.type) { |
---|
636 | case ButtonPress: |
---|
637 | /* Mouse button pressed/released over our window. We send upstream |
---|
638 | events for interactivity/navigation */ |
---|
639 | GST_DEBUG ("ximagesink button %d pressed over window at %d,%d", |
---|
640 | e.xbutton.button, e.xbutton.x, e.xbutton.x); |
---|
641 | gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink), |
---|
642 | "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y); |
---|
643 | break; |
---|
644 | case ButtonRelease: |
---|
645 | GST_DEBUG ("ximagesink button %d release over window at %d,%d", |
---|
646 | e.xbutton.button, e.xbutton.x, e.xbutton.x); |
---|
647 | gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink), |
---|
648 | "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y); |
---|
649 | break; |
---|
650 | case KeyPress: |
---|
651 | case KeyRelease: |
---|
652 | /* Key pressed/released over our window. We send upstream |
---|
653 | events for interactivity/navigation */ |
---|
654 | GST_DEBUG ("ximagesink key %d pressed over window at %d,%d", |
---|
655 | e.xkey.keycode, e.xkey.x, e.xkey.x); |
---|
656 | keysym = XKeycodeToKeysym (ximagesink->xcontext->disp, |
---|
657 | e.xkey.keycode, 0); |
---|
658 | if (keysym != NoSymbol) { |
---|
659 | gst_navigation_send_key_event (GST_NAVIGATION (ximagesink), |
---|
660 | e.type == KeyPress ? |
---|
661 | "key-press" : "key-release", XKeysymToString (keysym)); |
---|
662 | } else { |
---|
663 | gst_navigation_send_key_event (GST_NAVIGATION (ximagesink), |
---|
664 | e.type == KeyPress ? "key-press" : "key-release", "unknown"); |
---|
665 | } |
---|
666 | break; |
---|
667 | default: |
---|
668 | GST_DEBUG ("ximagesink unhandled X event (%d)", e.type); |
---|
669 | } |
---|
670 | g_mutex_lock (ximagesink->x_lock); |
---|
671 | } |
---|
672 | g_mutex_unlock (ximagesink->x_lock); |
---|
673 | } |
---|
674 | |
---|
675 | /* This function calculates the pixel aspect ratio based on the properties |
---|
676 | * in the xcontext structure and stores it there. */ |
---|
677 | static void |
---|
678 | gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext) |
---|
679 | { |
---|
680 | gint par[][2] = { |
---|
681 | {1, 1}, /* regular screen */ |
---|
682 | {16, 15}, /* PAL TV */ |
---|
683 | {11, 10}, /* 525 line Rec.601 video */ |
---|
684 | {54, 59} /* 625 line Rec.601 video */ |
---|
685 | }; |
---|
686 | gint i; |
---|
687 | gint index; |
---|
688 | gdouble ratio; |
---|
689 | gdouble delta; |
---|
690 | |
---|
691 | #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1]))) |
---|
692 | |
---|
693 | /* first calculate the "real" ratio based on the X values; |
---|
694 | * which is the "physical" w/h divided by the w/h in pixels of the display */ |
---|
695 | ratio = (gdouble) (xcontext->widthmm * xcontext->height) |
---|
696 | / (xcontext->heightmm * xcontext->width); |
---|
697 | |
---|
698 | /* DirectFB's X in 720x576 reports the physical dimensions wrong, so |
---|
699 | * override here */ |
---|
700 | if (xcontext->width == 720 && xcontext->height == 576) { |
---|
701 | ratio = 4.0 * 576 / (3.0 * 720); |
---|
702 | } |
---|
703 | GST_DEBUG ("calculated pixel aspect ratio: %f", ratio); |
---|
704 | |
---|
705 | /* now find the one from par[][2] with the lowest delta to the real one */ |
---|
706 | delta = DELTA (0); |
---|
707 | index = 0; |
---|
708 | |
---|
709 | for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) { |
---|
710 | gdouble this_delta = DELTA (i); |
---|
711 | |
---|
712 | if (this_delta < delta) { |
---|
713 | index = i; |
---|
714 | delta = this_delta; |
---|
715 | } |
---|
716 | } |
---|
717 | |
---|
718 | GST_DEBUG ("Decided on index %d (%d/%d)", index, |
---|
719 | par[index][0], par[index][1]); |
---|
720 | |
---|
721 | g_free (xcontext->par); |
---|
722 | xcontext->par = g_new0 (GValue, 1); |
---|
723 | g_value_init (xcontext->par, GST_TYPE_FRACTION); |
---|
724 | gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]); |
---|
725 | GST_DEBUG ("set xcontext PAR to %d/%d", |
---|
726 | gst_value_get_fraction_numerator (xcontext->par), |
---|
727 | gst_value_get_fraction_denominator (xcontext->par)); |
---|
728 | } |
---|
729 | |
---|
730 | /* This function gets the X Display and global info about it. Everything is |
---|
731 | stored in our object and will be cleaned when the object is disposed. Note |
---|
732 | here that caps for supported format are generated without any window or |
---|
733 | image creation */ |
---|
734 | static GstXContext * |
---|
735 | gst_ximagesink_xcontext_get (GstXImageSink * ximagesink) |
---|
736 | { |
---|
737 | GstXContext *xcontext = NULL; |
---|
738 | XPixmapFormatValues *px_formats = NULL; |
---|
739 | gint nb_formats = 0, i; |
---|
740 | |
---|
741 | g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL); |
---|
742 | |
---|
743 | xcontext = g_new0 (GstXContext, 1); |
---|
744 | |
---|
745 | g_mutex_lock (ximagesink->x_lock); |
---|
746 | |
---|
747 | xcontext->disp = XOpenDisplay (ximagesink->display_name); |
---|
748 | |
---|
749 | if (!xcontext->disp) { |
---|
750 | g_mutex_unlock (ximagesink->x_lock); |
---|
751 | g_free (xcontext); |
---|
752 | GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE, (NULL), |
---|
753 | ("Could not open display")); |
---|
754 | return NULL; |
---|
755 | } |
---|
756 | |
---|
757 | xcontext->screen = DefaultScreenOfDisplay (xcontext->disp); |
---|
758 | xcontext->screen_num = DefaultScreen (xcontext->disp); |
---|
759 | xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num); |
---|
760 | xcontext->root = DefaultRootWindow (xcontext->disp); |
---|
761 | xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num); |
---|
762 | xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num); |
---|
763 | xcontext->depth = DefaultDepthOfScreen (xcontext->screen); |
---|
764 | |
---|
765 | xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num); |
---|
766 | xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num); |
---|
767 | xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num); |
---|
768 | xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num); |
---|
769 | |
---|
770 | GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm", |
---|
771 | xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm); |
---|
772 | |
---|
773 | gst_ximagesink_calculate_pixel_aspect_ratio (xcontext); |
---|
774 | |
---|
775 | /* We get supported pixmap formats at supported depth */ |
---|
776 | px_formats = XListPixmapFormats (xcontext->disp, &nb_formats); |
---|
777 | |
---|
778 | if (!px_formats) { |
---|
779 | XCloseDisplay (xcontext->disp); |
---|
780 | g_mutex_unlock (ximagesink->x_lock); |
---|
781 | g_free (xcontext); |
---|
782 | return NULL; |
---|
783 | } |
---|
784 | |
---|
785 | /* We get bpp value corresponding to our running depth */ |
---|
786 | for (i = 0; i < nb_formats; i++) { |
---|
787 | if (px_formats[i].depth == xcontext->depth) |
---|
788 | xcontext->bpp = px_formats[i].bits_per_pixel; |
---|
789 | } |
---|
790 | |
---|
791 | XFree (px_formats); |
---|
792 | |
---|
793 | xcontext->endianness = |
---|
794 | (ImageByteOrder (xcontext->disp) == |
---|
795 | LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN; |
---|
796 | |
---|
797 | #ifdef HAVE_XSHM |
---|
798 | /* Search for XShm extension support */ |
---|
799 | if (XShmQueryExtension (xcontext->disp) && |
---|
800 | gst_ximagesink_check_xshm_calls (xcontext)) { |
---|
801 | xcontext->use_xshm = TRUE; |
---|
802 | GST_DEBUG ("ximagesink is using XShm extension"); |
---|
803 | } else { |
---|
804 | xcontext->use_xshm = FALSE; |
---|
805 | GST_DEBUG ("ximagesink is not using XShm extension"); |
---|
806 | } |
---|
807 | #endif /* HAVE_XSHM */ |
---|
808 | |
---|
809 | /* our caps system handles 24/32bpp RGB as big-endian. */ |
---|
810 | if ((xcontext->bpp == 24 || xcontext->bpp == 32) && |
---|
811 | xcontext->endianness == G_LITTLE_ENDIAN) { |
---|
812 | xcontext->endianness = G_BIG_ENDIAN; |
---|
813 | xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask); |
---|
814 | xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask); |
---|
815 | xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask); |
---|
816 | if (xcontext->bpp == 24) { |
---|
817 | xcontext->visual->red_mask >>= 8; |
---|
818 | xcontext->visual->green_mask >>= 8; |
---|
819 | xcontext->visual->blue_mask >>= 8; |
---|
820 | } |
---|
821 | } |
---|
822 | |
---|
823 | /* update object's par with calculated one if not set yet */ |
---|
824 | if (!ximagesink->par) { |
---|
825 | ximagesink->par = g_new0 (GValue, 1); |
---|
826 | gst_value_init_and_copy (ximagesink->par, xcontext->par); |
---|
827 | GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR"); |
---|
828 | } |
---|
829 | xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb", |
---|
830 | "bpp", G_TYPE_INT, xcontext->bpp, |
---|
831 | "depth", G_TYPE_INT, xcontext->depth, |
---|
832 | "endianness", G_TYPE_INT, xcontext->endianness, |
---|
833 | "red_mask", G_TYPE_INT, xcontext->visual->red_mask, |
---|
834 | "green_mask", G_TYPE_INT, xcontext->visual->green_mask, |
---|
835 | "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask, |
---|
836 | "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
---|
837 | "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
---|
838 | "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0, NULL); |
---|
839 | if (ximagesink->par) { |
---|
840 | int nom, den; |
---|
841 | |
---|
842 | nom = gst_value_get_fraction_numerator (ximagesink->par); |
---|
843 | den = gst_value_get_fraction_denominator (ximagesink->par); |
---|
844 | gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio", |
---|
845 | GST_TYPE_FRACTION, nom, den, NULL); |
---|
846 | } |
---|
847 | |
---|
848 | g_mutex_unlock (ximagesink->x_lock); |
---|
849 | |
---|
850 | return xcontext; |
---|
851 | } |
---|
852 | |
---|
853 | /* This function cleans the X context. Closing the Display and unrefing the |
---|
854 | caps for supported formats. */ |
---|
855 | static void |
---|
856 | gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink) |
---|
857 | { |
---|
858 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
859 | g_return_if_fail (ximagesink->xcontext != NULL); |
---|
860 | |
---|
861 | gst_caps_free (ximagesink->xcontext->caps); |
---|
862 | g_free (ximagesink->xcontext->par); |
---|
863 | g_free (ximagesink->par); |
---|
864 | ximagesink->par = NULL; |
---|
865 | |
---|
866 | g_mutex_lock (ximagesink->x_lock); |
---|
867 | |
---|
868 | XCloseDisplay (ximagesink->xcontext->disp); |
---|
869 | |
---|
870 | g_mutex_unlock (ximagesink->x_lock); |
---|
871 | |
---|
872 | g_free (ximagesink->xcontext); |
---|
873 | ximagesink->xcontext = NULL; |
---|
874 | } |
---|
875 | |
---|
876 | static void |
---|
877 | gst_ximagesink_imagepool_clear (GstXImageSink * ximagesink) |
---|
878 | { |
---|
879 | g_mutex_lock (ximagesink->pool_lock); |
---|
880 | |
---|
881 | while (ximagesink->image_pool) { |
---|
882 | GstXImage *ximage = ximagesink->image_pool->data; |
---|
883 | |
---|
884 | ximagesink->image_pool = g_slist_delete_link (ximagesink->image_pool, |
---|
885 | ximagesink->image_pool); |
---|
886 | gst_ximagesink_ximage_destroy (ximagesink, ximage); |
---|
887 | } |
---|
888 | |
---|
889 | g_mutex_unlock (ximagesink->pool_lock); |
---|
890 | } |
---|
891 | |
---|
892 | /* Element stuff */ |
---|
893 | |
---|
894 | static GstCaps * |
---|
895 | gst_ximagesink_fixate (GstPad * pad, const GstCaps * caps) |
---|
896 | { |
---|
897 | GstStructure *structure; |
---|
898 | GstCaps *newcaps; |
---|
899 | |
---|
900 | if (gst_caps_get_size (caps) > 1) |
---|
901 | return NULL; |
---|
902 | |
---|
903 | newcaps = gst_caps_copy (caps); |
---|
904 | structure = gst_caps_get_structure (newcaps, 0); |
---|
905 | |
---|
906 | /* if par is set and either w or h is set, we can set the other */ |
---|
907 | |
---|
908 | if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 320)) { |
---|
909 | return newcaps; |
---|
910 | } |
---|
911 | if (gst_caps_structure_fixate_field_nearest_int (structure, "height", 240)) { |
---|
912 | return newcaps; |
---|
913 | } |
---|
914 | if (gst_caps_structure_fixate_field_nearest_double (structure, "framerate", |
---|
915 | 30.0)) { |
---|
916 | return newcaps; |
---|
917 | } |
---|
918 | |
---|
919 | gst_caps_free (newcaps); |
---|
920 | return NULL; |
---|
921 | } |
---|
922 | |
---|
923 | static GstCaps * |
---|
924 | gst_ximagesink_getcaps (GstPad * pad) |
---|
925 | { |
---|
926 | GstXImageSink *ximagesink; |
---|
927 | GstCaps *caps; |
---|
928 | int i; |
---|
929 | |
---|
930 | ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad)); |
---|
931 | |
---|
932 | if (ximagesink->xcontext) |
---|
933 | return gst_caps_copy (ximagesink->xcontext->caps); |
---|
934 | |
---|
935 | /* get a template copy and add the pixel aspect ratio */ |
---|
936 | caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); |
---|
937 | for (i = 0; i < gst_caps_get_size (caps); ++i) { |
---|
938 | GstStructure *structure = gst_caps_get_structure (caps, i); |
---|
939 | |
---|
940 | if (ximagesink->par) { |
---|
941 | int nom, den; |
---|
942 | |
---|
943 | nom = gst_value_get_fraction_numerator (ximagesink->par); |
---|
944 | den = gst_value_get_fraction_denominator (ximagesink->par); |
---|
945 | gst_structure_set (structure, "pixel-aspect-ratio", |
---|
946 | GST_TYPE_FRACTION, nom, den, NULL); |
---|
947 | } |
---|
948 | } |
---|
949 | return caps; |
---|
950 | } |
---|
951 | |
---|
952 | static GstPadLinkReturn |
---|
953 | gst_ximagesink_sink_link (GstPad * pad, const GstCaps * caps) |
---|
954 | { |
---|
955 | GstXImageSink *ximagesink; |
---|
956 | gboolean ret; |
---|
957 | GstStructure *structure; |
---|
958 | const GValue *par; |
---|
959 | |
---|
960 | ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad)); |
---|
961 | |
---|
962 | if (!ximagesink->xcontext) |
---|
963 | return GST_PAD_LINK_DELAYED; |
---|
964 | |
---|
965 | GST_DEBUG_OBJECT (ximagesink, |
---|
966 | "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %" |
---|
967 | GST_PTR_FORMAT, ximagesink->xcontext->caps, caps); |
---|
968 | |
---|
969 | structure = gst_caps_get_structure (caps, 0); |
---|
970 | ret = gst_structure_get_int (structure, "width", |
---|
971 | &(GST_VIDEOSINK_WIDTH (ximagesink))); |
---|
972 | ret &= gst_structure_get_int (structure, "height", |
---|
973 | &(GST_VIDEOSINK_HEIGHT (ximagesink))); |
---|
974 | ret &= gst_structure_get_double (structure, |
---|
975 | "framerate", &ximagesink->framerate); |
---|
976 | if (!ret) |
---|
977 | return GST_PAD_LINK_REFUSED; |
---|
978 | |
---|
979 | /* if the caps contain pixel-aspect-ratio, they have to match ours, |
---|
980 | * otherwise linking should fail */ |
---|
981 | par = gst_structure_get_value (structure, "pixel-aspect-ratio"); |
---|
982 | if (par) { |
---|
983 | if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) { |
---|
984 | GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match"); |
---|
985 | return GST_PAD_LINK_REFUSED; |
---|
986 | } |
---|
987 | } |
---|
988 | |
---|
989 | /* Creating our window and our image */ |
---|
990 | g_assert (GST_VIDEOSINK_WIDTH (ximagesink) > 0); |
---|
991 | g_assert (GST_VIDEOSINK_HEIGHT (ximagesink) > 0); |
---|
992 | if (!ximagesink->xwindow) { |
---|
993 | ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink, |
---|
994 | GST_VIDEOSINK_WIDTH (ximagesink), GST_VIDEOSINK_HEIGHT (ximagesink)); |
---|
995 | } else { |
---|
996 | if (ximagesink->xwindow->internal) { |
---|
997 | gst_ximagesink_xwindow_resize (ximagesink, ximagesink->xwindow, |
---|
998 | GST_VIDEOSINK_WIDTH (ximagesink), GST_VIDEOSINK_HEIGHT (ximagesink)); |
---|
999 | } |
---|
1000 | } |
---|
1001 | |
---|
1002 | /* If our ximage has changed we destroy it, next chain iteration will create |
---|
1003 | a new one */ |
---|
1004 | if ((ximagesink->ximage) && |
---|
1005 | ((GST_VIDEOSINK_WIDTH (ximagesink) != ximagesink->ximage->width) || |
---|
1006 | (GST_VIDEOSINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) { |
---|
1007 | gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage); |
---|
1008 | ximagesink->ximage = NULL; |
---|
1009 | } |
---|
1010 | |
---|
1011 | gst_x_overlay_got_desired_size (GST_X_OVERLAY (ximagesink), |
---|
1012 | GST_VIDEOSINK_WIDTH (ximagesink), GST_VIDEOSINK_HEIGHT (ximagesink)); |
---|
1013 | |
---|
1014 | return GST_PAD_LINK_OK; |
---|
1015 | } |
---|
1016 | |
---|
1017 | static GstElementStateReturn |
---|
1018 | gst_ximagesink_change_state (GstElement * element) |
---|
1019 | { |
---|
1020 | GstXImageSink *ximagesink; |
---|
1021 | |
---|
1022 | ximagesink = GST_XIMAGESINK (element); |
---|
1023 | |
---|
1024 | switch (GST_STATE_TRANSITION (element)) { |
---|
1025 | case GST_STATE_NULL_TO_READY: |
---|
1026 | /* Initializing the XContext */ |
---|
1027 | if (!ximagesink->xcontext) |
---|
1028 | ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink); |
---|
1029 | if (!ximagesink->xcontext) |
---|
1030 | return GST_STATE_FAILURE; |
---|
1031 | /* call XSynchronize with the current value of synchronous */ |
---|
1032 | GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s", |
---|
1033 | ximagesink->synchronous ? "TRUE" : "FALSE"); |
---|
1034 | XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous); |
---|
1035 | break; |
---|
1036 | case GST_STATE_READY_TO_PAUSED: |
---|
1037 | if (ximagesink->xwindow) |
---|
1038 | gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow); |
---|
1039 | ximagesink->time = 0; |
---|
1040 | break; |
---|
1041 | case GST_STATE_PAUSED_TO_PLAYING: |
---|
1042 | break; |
---|
1043 | case GST_STATE_PLAYING_TO_PAUSED: |
---|
1044 | break; |
---|
1045 | case GST_STATE_PAUSED_TO_READY: |
---|
1046 | ximagesink->framerate = 0; |
---|
1047 | ximagesink->sw_scaling_failed = FALSE; |
---|
1048 | GST_VIDEOSINK_WIDTH (ximagesink) = 0; |
---|
1049 | GST_VIDEOSINK_HEIGHT (ximagesink) = 0; |
---|
1050 | break; |
---|
1051 | case GST_STATE_READY_TO_NULL: |
---|
1052 | if (ximagesink->ximage) { |
---|
1053 | gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage); |
---|
1054 | ximagesink->ximage = NULL; |
---|
1055 | } |
---|
1056 | |
---|
1057 | if (ximagesink->image_pool) |
---|
1058 | gst_ximagesink_imagepool_clear (ximagesink); |
---|
1059 | |
---|
1060 | if (ximagesink->xwindow) { |
---|
1061 | gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow); |
---|
1062 | ximagesink->xwindow = NULL; |
---|
1063 | } |
---|
1064 | |
---|
1065 | if (ximagesink->xcontext) { |
---|
1066 | gst_ximagesink_xcontext_clear (ximagesink); |
---|
1067 | ximagesink->xcontext = NULL; |
---|
1068 | } |
---|
1069 | break; |
---|
1070 | } |
---|
1071 | |
---|
1072 | if (GST_ELEMENT_CLASS (parent_class)->change_state) |
---|
1073 | return GST_ELEMENT_CLASS (parent_class)->change_state (element); |
---|
1074 | |
---|
1075 | return GST_STATE_SUCCESS; |
---|
1076 | } |
---|
1077 | |
---|
1078 | static void |
---|
1079 | gst_ximagesink_chain (GstPad * pad, GstData * data) |
---|
1080 | { |
---|
1081 | GstBuffer *buf = GST_BUFFER (data); |
---|
1082 | GstXImageSink *ximagesink; |
---|
1083 | |
---|
1084 | g_return_if_fail (GST_IS_PAD (pad)); |
---|
1085 | g_return_if_fail (buf != NULL); |
---|
1086 | |
---|
1087 | ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad)); |
---|
1088 | |
---|
1089 | if (GST_IS_EVENT (data)) { |
---|
1090 | gst_pad_event_default (pad, GST_EVENT (data)); |
---|
1091 | return; |
---|
1092 | } |
---|
1093 | |
---|
1094 | buf = GST_BUFFER (data); |
---|
1095 | /* update time */ |
---|
1096 | if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { |
---|
1097 | ximagesink->time = GST_BUFFER_TIMESTAMP (buf); |
---|
1098 | } |
---|
1099 | GST_LOG_OBJECT (ximagesink, "clock wait: %" GST_TIME_FORMAT, |
---|
1100 | GST_TIME_ARGS (ximagesink->time)); |
---|
1101 | |
---|
1102 | if (GST_VIDEOSINK_CLOCK (ximagesink)) { |
---|
1103 | gst_element_wait (GST_ELEMENT (ximagesink), ximagesink->time); |
---|
1104 | } |
---|
1105 | |
---|
1106 | /* If this buffer has been allocated using our buffer management we simply |
---|
1107 | put the ximage which is in the PRIVATE pointer */ |
---|
1108 | if (GST_BUFFER_FREE_DATA_FUNC (buf) == gst_ximagesink_buffer_free) { |
---|
1109 | GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly"); |
---|
1110 | gst_ximagesink_ximage_put (ximagesink, GST_BUFFER_PRIVATE (buf)); |
---|
1111 | } else { |
---|
1112 | /* Else we have to copy the data into our private image, */ |
---|
1113 | /* if we have one... */ |
---|
1114 | GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it"); |
---|
1115 | if (!ximagesink->ximage) { |
---|
1116 | GST_DEBUG_OBJECT (ximagesink, "creating our ximage"); |
---|
1117 | ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink, |
---|
1118 | GST_VIDEOSINK_WIDTH (ximagesink), GST_VIDEOSINK_HEIGHT (ximagesink)); |
---|
1119 | if (!ximagesink->ximage) { |
---|
1120 | /* No image available. That's very bad ! */ |
---|
1121 | gst_buffer_unref (buf); |
---|
1122 | GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL), |
---|
1123 | ("Failed creating an XImage in ximagesink chain function.")); |
---|
1124 | return; |
---|
1125 | } |
---|
1126 | } |
---|
1127 | |
---|
1128 | memcpy (ximagesink->ximage->ximage->data, |
---|
1129 | GST_BUFFER_DATA (buf), |
---|
1130 | MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size)); |
---|
1131 | gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage); |
---|
1132 | } |
---|
1133 | |
---|
1134 | /* set correct time for next buffer */ |
---|
1135 | if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf) && ximagesink->framerate > 0) { |
---|
1136 | ximagesink->time += GST_SECOND / ximagesink->framerate; |
---|
1137 | } |
---|
1138 | |
---|
1139 | gst_buffer_unref (buf); |
---|
1140 | |
---|
1141 | gst_ximagesink_handle_xevents (ximagesink, pad); |
---|
1142 | } |
---|
1143 | |
---|
1144 | /* Buffer management */ |
---|
1145 | |
---|
1146 | static void |
---|
1147 | gst_ximagesink_buffer_free (GstBuffer * buffer) |
---|
1148 | { |
---|
1149 | GstXImageSink *ximagesink; |
---|
1150 | GstXImage *ximage; |
---|
1151 | |
---|
1152 | ximage = GST_BUFFER_PRIVATE (buffer); |
---|
1153 | |
---|
1154 | g_assert (GST_IS_XIMAGESINK (ximage->ximagesink)); |
---|
1155 | ximagesink = ximage->ximagesink; |
---|
1156 | |
---|
1157 | /* If our geometry changed we can't reuse that image. */ |
---|
1158 | if ((ximage->width != GST_VIDEOSINK_WIDTH (ximagesink)) || |
---|
1159 | (ximage->height != GST_VIDEOSINK_HEIGHT (ximagesink))) |
---|
1160 | gst_ximagesink_ximage_destroy (ximagesink, ximage); |
---|
1161 | else { |
---|
1162 | /* In that case we can reuse the image and add it to our image pool. */ |
---|
1163 | g_mutex_lock (ximagesink->pool_lock); |
---|
1164 | ximagesink->image_pool = g_slist_prepend (ximagesink->image_pool, ximage); |
---|
1165 | g_mutex_unlock (ximagesink->pool_lock); |
---|
1166 | } |
---|
1167 | } |
---|
1168 | |
---|
1169 | static GstBuffer * |
---|
1170 | gst_ximagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size) |
---|
1171 | { |
---|
1172 | GstXImageSink *ximagesink; |
---|
1173 | GstBuffer *buffer; |
---|
1174 | GstXImage *ximage = NULL; |
---|
1175 | gboolean not_found = TRUE; |
---|
1176 | |
---|
1177 | ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad)); |
---|
1178 | |
---|
1179 | g_mutex_lock (ximagesink->pool_lock); |
---|
1180 | |
---|
1181 | /* Walking through the pool cleaning unsuable images and searching for a |
---|
1182 | suitable one */ |
---|
1183 | while (not_found && ximagesink->image_pool) { |
---|
1184 | ximage = ximagesink->image_pool->data; |
---|
1185 | |
---|
1186 | if (ximage) { |
---|
1187 | /* Removing from the pool */ |
---|
1188 | ximagesink->image_pool = g_slist_delete_link (ximagesink->image_pool, |
---|
1189 | ximagesink->image_pool); |
---|
1190 | |
---|
1191 | if ((ximage->width != GST_VIDEOSINK_WIDTH (ximagesink)) || |
---|
1192 | (ximage->height != GST_VIDEOSINK_HEIGHT (ximagesink))) { |
---|
1193 | /* This image is unusable. Destroying... */ |
---|
1194 | gst_ximagesink_ximage_destroy (ximagesink, ximage); |
---|
1195 | ximage = NULL; |
---|
1196 | } else { |
---|
1197 | /* We found a suitable image */ |
---|
1198 | break; |
---|
1199 | } |
---|
1200 | } |
---|
1201 | } |
---|
1202 | |
---|
1203 | g_mutex_unlock (ximagesink->pool_lock); |
---|
1204 | |
---|
1205 | if (!ximage) { |
---|
1206 | /* We found no suitable image in the pool. Creating... */ |
---|
1207 | GST_DEBUG_OBJECT (ximagesink, "no usable image in pool, creating ximage"); |
---|
1208 | ximage = gst_ximagesink_ximage_new (ximagesink, |
---|
1209 | GST_VIDEOSINK_WIDTH (ximagesink), GST_VIDEOSINK_HEIGHT (ximagesink)); |
---|
1210 | } |
---|
1211 | |
---|
1212 | if (ximage) { |
---|
1213 | buffer = gst_buffer_new (); |
---|
1214 | |
---|
1215 | /* Storing some pointers in the buffer */ |
---|
1216 | GST_BUFFER_PRIVATE (buffer) = ximage; |
---|
1217 | |
---|
1218 | GST_BUFFER_DATA (buffer) = ximage->ximage->data; |
---|
1219 | GST_BUFFER_FREE_DATA_FUNC (buffer) = gst_ximagesink_buffer_free; |
---|
1220 | GST_BUFFER_SIZE (buffer) = ximage->size; |
---|
1221 | return buffer; |
---|
1222 | } else |
---|
1223 | return NULL; |
---|
1224 | } |
---|
1225 | |
---|
1226 | /* Interfaces stuff */ |
---|
1227 | |
---|
1228 | static gboolean |
---|
1229 | gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type) |
---|
1230 | { |
---|
1231 | g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY); |
---|
1232 | return TRUE; |
---|
1233 | } |
---|
1234 | |
---|
1235 | static void |
---|
1236 | gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass) |
---|
1237 | { |
---|
1238 | klass->supported = gst_ximagesink_interface_supported; |
---|
1239 | } |
---|
1240 | |
---|
1241 | static void |
---|
1242 | gst_ximagesink_navigation_send_event (GstNavigation * navigation, |
---|
1243 | GstStructure * structure) |
---|
1244 | { |
---|
1245 | GstXImageSink *ximagesink = GST_XIMAGESINK (navigation); |
---|
1246 | GstEvent *event; |
---|
1247 | gint x_offset, y_offset; |
---|
1248 | double x, y; |
---|
1249 | |
---|
1250 | event = gst_event_new (GST_EVENT_NAVIGATION); |
---|
1251 | event->event_data.structure.structure = structure; |
---|
1252 | |
---|
1253 | /* We are not converting the pointer coordinates as there's no hardware |
---|
1254 | scaling done here. The only possible scaling is done by videoscale and |
---|
1255 | videoscale will have to catch those events and tranform the coordinates |
---|
1256 | to match the applied scaling. So here we just add the offset if the image |
---|
1257 | is centered in the window. */ |
---|
1258 | |
---|
1259 | x_offset = ximagesink->xwindow->width - GST_VIDEOSINK_WIDTH (ximagesink); |
---|
1260 | y_offset = ximagesink->xwindow->height - GST_VIDEOSINK_HEIGHT (ximagesink); |
---|
1261 | |
---|
1262 | if (gst_structure_get_double (structure, "pointer_x", &x)) { |
---|
1263 | x += x_offset; |
---|
1264 | gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL); |
---|
1265 | } |
---|
1266 | if (gst_structure_get_double (structure, "pointer_y", &y)) { |
---|
1267 | y += y_offset; |
---|
1268 | gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL); |
---|
1269 | } |
---|
1270 | |
---|
1271 | gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD (ximagesink)), event); |
---|
1272 | } |
---|
1273 | |
---|
1274 | static void |
---|
1275 | gst_ximagesink_navigation_init (GstNavigationInterface * iface) |
---|
1276 | { |
---|
1277 | iface->send_event = gst_ximagesink_navigation_send_event; |
---|
1278 | } |
---|
1279 | |
---|
1280 | static void |
---|
1281 | gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id) |
---|
1282 | { |
---|
1283 | GstXImageSink *ximagesink = GST_XIMAGESINK (overlay); |
---|
1284 | GstXWindow *xwindow = NULL; |
---|
1285 | XWindowAttributes attr; |
---|
1286 | |
---|
1287 | g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); |
---|
1288 | |
---|
1289 | /* If we already use that window return */ |
---|
1290 | if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) |
---|
1291 | return; |
---|
1292 | |
---|
1293 | /* If the element has not initialized the X11 context try to do so */ |
---|
1294 | if (!ximagesink->xcontext) |
---|
1295 | ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink); |
---|
1296 | |
---|
1297 | if (!ximagesink->xcontext) { |
---|
1298 | g_warning ("ximagesink was unable to obtain the X11 context."); |
---|
1299 | return; |
---|
1300 | } |
---|
1301 | |
---|
1302 | /* Clear image pool as the images are unusable anyway */ |
---|
1303 | gst_ximagesink_imagepool_clear (ximagesink); |
---|
1304 | |
---|
1305 | /* Clear the ximage */ |
---|
1306 | if (ximagesink->ximage) { |
---|
1307 | gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage); |
---|
1308 | ximagesink->ximage = NULL; |
---|
1309 | } |
---|
1310 | |
---|
1311 | /* If a window is there already we destroy it */ |
---|
1312 | if (ximagesink->xwindow) { |
---|
1313 | gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow); |
---|
1314 | ximagesink->xwindow = NULL; |
---|
1315 | } |
---|
1316 | |
---|
1317 | /* If the xid is 0 we go back to an internal window */ |
---|
1318 | if (xwindow_id == 0) { |
---|
1319 | /* If no width/height caps nego did not happen window will be created |
---|
1320 | during caps nego then */ |
---|
1321 | if (GST_VIDEOSINK_WIDTH (ximagesink) && GST_VIDEOSINK_HEIGHT (ximagesink)) { |
---|
1322 | xwindow = gst_ximagesink_xwindow_new (ximagesink, |
---|
1323 | GST_VIDEOSINK_WIDTH (ximagesink), GST_VIDEOSINK_HEIGHT (ximagesink)); |
---|
1324 | } |
---|
1325 | } else { |
---|
1326 | xwindow = g_new0 (GstXWindow, 1); |
---|
1327 | |
---|
1328 | xwindow->win = xwindow_id; |
---|
1329 | |
---|
1330 | /* We get window geometry, set the event we want to receive, |
---|
1331 | and create a GC */ |
---|
1332 | g_mutex_lock (ximagesink->x_lock); |
---|
1333 | XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr); |
---|
1334 | xwindow->width = attr.width; |
---|
1335 | xwindow->height = attr.height; |
---|
1336 | xwindow->internal = FALSE; |
---|
1337 | XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask | |
---|
1338 | StructureNotifyMask | PointerMotionMask | KeyPressMask | |
---|
1339 | KeyReleaseMask); |
---|
1340 | |
---|
1341 | xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL); |
---|
1342 | g_mutex_unlock (ximagesink->x_lock); |
---|
1343 | |
---|
1344 | /* If that new window geometry differs from our one we try to |
---|
1345 | renegotiate caps */ |
---|
1346 | if (gst_pad_is_negotiated (GST_VIDEOSINK_PAD (ximagesink)) && |
---|
1347 | (xwindow->width != GST_VIDEOSINK_WIDTH (ximagesink) || |
---|
1348 | xwindow->height != GST_VIDEOSINK_HEIGHT (ximagesink))) { |
---|
1349 | GstPadLinkReturn r; |
---|
1350 | GstCaps *caps; |
---|
1351 | |
---|
1352 | caps = gst_caps_new_simple ("video/x-raw-rgb", |
---|
1353 | "bpp", G_TYPE_INT, ximagesink->xcontext->bpp, |
---|
1354 | "depth", G_TYPE_INT, ximagesink->xcontext->depth, |
---|
1355 | "endianness", G_TYPE_INT, ximagesink->xcontext->endianness, |
---|
1356 | "red_mask", G_TYPE_INT, ximagesink->xcontext->visual->red_mask, |
---|
1357 | "green_mask", G_TYPE_INT, |
---|
1358 | ximagesink->xcontext->visual->green_mask, |
---|
1359 | "blue_mask", G_TYPE_INT, |
---|
1360 | ximagesink->xcontext->visual->blue_mask, |
---|
1361 | "width", G_TYPE_INT, xwindow->width, |
---|
1362 | "height", G_TYPE_INT, xwindow->height, |
---|
1363 | "framerate", G_TYPE_DOUBLE, ximagesink->framerate, NULL); |
---|
1364 | |
---|
1365 | if (ximagesink->par) { |
---|
1366 | int nom, den; |
---|
1367 | |
---|
1368 | nom = gst_value_get_fraction_numerator (ximagesink->par); |
---|
1369 | den = gst_value_get_fraction_denominator (ximagesink->par); |
---|
1370 | gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, |
---|
1371 | nom, den, NULL); |
---|
1372 | } |
---|
1373 | r = gst_pad_try_set_caps (GST_VIDEOSINK_PAD (ximagesink), caps); |
---|
1374 | |
---|
1375 | /* If caps nego succeded updating our size */ |
---|
1376 | if ((r == GST_PAD_LINK_OK) || (r == GST_PAD_LINK_DONE)) { |
---|
1377 | GST_VIDEOSINK_WIDTH (ximagesink) = xwindow->width; |
---|
1378 | GST_VIDEOSINK_HEIGHT (ximagesink) = xwindow->height; |
---|
1379 | } |
---|
1380 | } |
---|
1381 | } |
---|
1382 | |
---|
1383 | if (xwindow) |
---|
1384 | ximagesink->xwindow = xwindow; |
---|
1385 | } |
---|
1386 | |
---|
1387 | static void |
---|
1388 | gst_ximagesink_get_desired_size (GstXOverlay * overlay, |
---|
1389 | guint * width, guint * height) |
---|
1390 | { |
---|
1391 | GstXImageSink *ximagesink = GST_XIMAGESINK (overlay); |
---|
1392 | |
---|
1393 | *width = GST_VIDEOSINK_WIDTH (ximagesink); |
---|
1394 | *height = GST_VIDEOSINK_HEIGHT (ximagesink); |
---|
1395 | } |
---|
1396 | |
---|
1397 | static void |
---|
1398 | gst_ximagesink_expose (GstXOverlay * overlay) |
---|
1399 | { |
---|
1400 | GstXImageSink *ximagesink = GST_XIMAGESINK (overlay); |
---|
1401 | |
---|
1402 | if (!ximagesink->xwindow) |
---|
1403 | return; |
---|
1404 | |
---|
1405 | gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow); |
---|
1406 | |
---|
1407 | /* We don't act on internal window from outside that could cause some thread |
---|
1408 | race with the video sink own thread checking for configure event */ |
---|
1409 | if (ximagesink->xwindow->internal) |
---|
1410 | return; |
---|
1411 | |
---|
1412 | gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow); |
---|
1413 | |
---|
1414 | if (ximagesink->cur_image) |
---|
1415 | gst_ximagesink_ximage_put (ximagesink, ximagesink->cur_image); |
---|
1416 | } |
---|
1417 | |
---|
1418 | static void |
---|
1419 | gst_ximagesink_xoverlay_init (GstXOverlayClass * iface) |
---|
1420 | { |
---|
1421 | iface->set_xwindow_id = gst_ximagesink_set_xwindow_id; |
---|
1422 | iface->get_desired_size = gst_ximagesink_get_desired_size; |
---|
1423 | iface->expose = gst_ximagesink_expose; |
---|
1424 | } |
---|
1425 | |
---|
1426 | /* =========================================== */ |
---|
1427 | /* */ |
---|
1428 | /* Init & Class init */ |
---|
1429 | /* */ |
---|
1430 | /* =========================================== */ |
---|
1431 | |
---|
1432 | static void |
---|
1433 | gst_ximagesink_set_property (GObject * object, guint prop_id, |
---|
1434 | const GValue * value, GParamSpec * pspec) |
---|
1435 | { |
---|
1436 | GstXImageSink *ximagesink; |
---|
1437 | |
---|
1438 | g_return_if_fail (GST_IS_XIMAGESINK (object)); |
---|
1439 | |
---|
1440 | ximagesink = GST_XIMAGESINK (object); |
---|
1441 | |
---|
1442 | switch (prop_id) { |
---|
1443 | case ARG_DISPLAY: |
---|
1444 | ximagesink->display_name = g_strdup (g_value_get_string (value)); |
---|
1445 | break; |
---|
1446 | case ARG_SYNCHRONOUS: |
---|
1447 | ximagesink->synchronous = g_value_get_boolean (value); |
---|
1448 | if (ximagesink->xcontext) { |
---|
1449 | GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s", |
---|
1450 | ximagesink->synchronous ? "TRUE" : "FALSE"); |
---|
1451 | XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous); |
---|
1452 | } |
---|
1453 | break; |
---|
1454 | case ARG_PIXEL_ASPECT_RATIO: |
---|
1455 | g_free (ximagesink->par); |
---|
1456 | ximagesink->par = g_new0 (GValue, 1); |
---|
1457 | g_value_init (ximagesink->par, GST_TYPE_FRACTION); |
---|
1458 | if (!g_value_transform (value, ximagesink->par)) { |
---|
1459 | g_warning ("Could not transform string to aspect ratio"); |
---|
1460 | gst_value_set_fraction (ximagesink->par, 1, 1); |
---|
1461 | } |
---|
1462 | GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d", |
---|
1463 | gst_value_get_fraction_numerator (ximagesink->par), |
---|
1464 | gst_value_get_fraction_denominator (ximagesink->par)); |
---|
1465 | break; |
---|
1466 | default: |
---|
1467 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
---|
1468 | break; |
---|
1469 | } |
---|
1470 | } |
---|
1471 | |
---|
1472 | static void |
---|
1473 | gst_ximagesink_get_property (GObject * object, guint prop_id, |
---|
1474 | GValue * value, GParamSpec * pspec) |
---|
1475 | { |
---|
1476 | GstXImageSink *ximagesink; |
---|
1477 | |
---|
1478 | g_return_if_fail (GST_IS_XIMAGESINK (object)); |
---|
1479 | |
---|
1480 | ximagesink = GST_XIMAGESINK (object); |
---|
1481 | |
---|
1482 | switch (prop_id) { |
---|
1483 | case ARG_DISPLAY: |
---|
1484 | g_value_set_string (value, g_strdup (ximagesink->display_name)); |
---|
1485 | break; |
---|
1486 | case ARG_SYNCHRONOUS: |
---|
1487 | g_value_set_boolean (value, ximagesink->synchronous); |
---|
1488 | break; |
---|
1489 | case ARG_PIXEL_ASPECT_RATIO: |
---|
1490 | if (ximagesink->par) |
---|
1491 | g_value_transform (ximagesink->par, value); |
---|
1492 | break; |
---|
1493 | default: |
---|
1494 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
---|
1495 | break; |
---|
1496 | } |
---|
1497 | } |
---|
1498 | |
---|
1499 | static void |
---|
1500 | gst_ximagesink_finalize (GObject * object) |
---|
1501 | { |
---|
1502 | GstXImageSink *ximagesink; |
---|
1503 | |
---|
1504 | ximagesink = GST_XIMAGESINK (object); |
---|
1505 | |
---|
1506 | if (ximagesink->display_name) { |
---|
1507 | g_free (ximagesink->display_name); |
---|
1508 | ximagesink->display_name = NULL; |
---|
1509 | } |
---|
1510 | |
---|
1511 | if (ximagesink->par) { |
---|
1512 | g_free (ximagesink->par); |
---|
1513 | ximagesink->par = NULL; |
---|
1514 | } |
---|
1515 | if (ximagesink->x_lock) { |
---|
1516 | g_mutex_free (ximagesink->x_lock); |
---|
1517 | ximagesink->x_lock = NULL; |
---|
1518 | } |
---|
1519 | if (ximagesink->pool_lock) { |
---|
1520 | g_mutex_free (ximagesink->pool_lock); |
---|
1521 | ximagesink->pool_lock = NULL; |
---|
1522 | } |
---|
1523 | |
---|
1524 | G_OBJECT_CLASS (parent_class)->finalize (object); |
---|
1525 | } |
---|
1526 | |
---|
1527 | static void |
---|
1528 | gst_ximagesink_init (GstXImageSink * ximagesink) |
---|
1529 | { |
---|
1530 | GST_VIDEOSINK_PAD (ximagesink) = |
---|
1531 | gst_pad_new_from_template (gst_static_pad_template_get |
---|
1532 | (&gst_ximagesink_sink_template_factory), "sink"); |
---|
1533 | |
---|
1534 | gst_element_add_pad (GST_ELEMENT (ximagesink), |
---|
1535 | GST_VIDEOSINK_PAD (ximagesink)); |
---|
1536 | |
---|
1537 | gst_pad_set_chain_function (GST_VIDEOSINK_PAD (ximagesink), |
---|
1538 | gst_ximagesink_chain); |
---|
1539 | gst_pad_set_link_function (GST_VIDEOSINK_PAD (ximagesink), |
---|
1540 | gst_ximagesink_sink_link); |
---|
1541 | gst_pad_set_getcaps_function (GST_VIDEOSINK_PAD (ximagesink), |
---|
1542 | gst_ximagesink_getcaps); |
---|
1543 | gst_pad_set_fixate_function (GST_VIDEOSINK_PAD (ximagesink), |
---|
1544 | gst_ximagesink_fixate); |
---|
1545 | gst_pad_set_bufferalloc_function (GST_VIDEOSINK_PAD (ximagesink), |
---|
1546 | gst_ximagesink_buffer_alloc); |
---|
1547 | |
---|
1548 | ximagesink->display_name = NULL; |
---|
1549 | ximagesink->xcontext = NULL; |
---|
1550 | ximagesink->xwindow = NULL; |
---|
1551 | ximagesink->ximage = NULL; |
---|
1552 | ximagesink->cur_image = NULL; |
---|
1553 | |
---|
1554 | ximagesink->framerate = 0; |
---|
1555 | |
---|
1556 | ximagesink->x_lock = g_mutex_new (); |
---|
1557 | |
---|
1558 | |
---|
1559 | ximagesink->image_pool = NULL; |
---|
1560 | ximagesink->pool_lock = g_mutex_new (); |
---|
1561 | |
---|
1562 | ximagesink->sw_scaling_failed = FALSE; |
---|
1563 | ximagesink->synchronous = FALSE; |
---|
1564 | |
---|
1565 | ximagesink->par = NULL; |
---|
1566 | |
---|
1567 | GST_FLAG_SET (ximagesink, GST_ELEMENT_THREAD_SUGGESTED); |
---|
1568 | GST_FLAG_SET (ximagesink, GST_ELEMENT_EVENT_AWARE); |
---|
1569 | } |
---|
1570 | |
---|
1571 | static void |
---|
1572 | gst_ximagesink_base_init (gpointer g_class) |
---|
1573 | { |
---|
1574 | GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
---|
1575 | |
---|
1576 | gst_element_class_set_details (element_class, &gst_ximagesink_details); |
---|
1577 | |
---|
1578 | gst_element_class_add_pad_template (element_class, |
---|
1579 | gst_static_pad_template_get (&gst_ximagesink_sink_template_factory)); |
---|
1580 | } |
---|
1581 | |
---|
1582 | static void |
---|
1583 | gst_ximagesink_class_init (GstXImageSinkClass * klass) |
---|
1584 | { |
---|
1585 | GObjectClass *gobject_class; |
---|
1586 | GstElementClass *gstelement_class; |
---|
1587 | |
---|
1588 | gobject_class = (GObjectClass *) klass; |
---|
1589 | gstelement_class = (GstElementClass *) klass; |
---|
1590 | |
---|
1591 | parent_class = g_type_class_ref (GST_TYPE_VIDEOSINK); |
---|
1592 | |
---|
1593 | g_object_class_install_property (gobject_class, ARG_DISPLAY, |
---|
1594 | g_param_spec_string ("display", "Display", "X Display name", |
---|
1595 | NULL, G_PARAM_READWRITE)); |
---|
1596 | g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS, |
---|
1597 | g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs " |
---|
1598 | "the X display in synchronous mode. (used only for debugging)", FALSE, |
---|
1599 | G_PARAM_READWRITE)); |
---|
1600 | g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO, |
---|
1601 | g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio", |
---|
1602 | "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE)); |
---|
1603 | |
---|
1604 | gobject_class->finalize = gst_ximagesink_finalize; |
---|
1605 | gobject_class->set_property = gst_ximagesink_set_property; |
---|
1606 | gobject_class->get_property = gst_ximagesink_get_property; |
---|
1607 | |
---|
1608 | gstelement_class->change_state = gst_ximagesink_change_state; |
---|
1609 | } |
---|
1610 | |
---|
1611 | /* ============================================================= */ |
---|
1612 | /* */ |
---|
1613 | /* Public Methods */ |
---|
1614 | /* */ |
---|
1615 | /* ============================================================= */ |
---|
1616 | |
---|
1617 | /* =========================================== */ |
---|
1618 | /* */ |
---|
1619 | /* Object typing & Creation */ |
---|
1620 | /* */ |
---|
1621 | /* =========================================== */ |
---|
1622 | |
---|
1623 | GType |
---|
1624 | gst_ximagesink_get_type (void) |
---|
1625 | { |
---|
1626 | static GType ximagesink_type = 0; |
---|
1627 | |
---|
1628 | if (!ximagesink_type) { |
---|
1629 | static const GTypeInfo ximagesink_info = { |
---|
1630 | sizeof (GstXImageSinkClass), |
---|
1631 | gst_ximagesink_base_init, |
---|
1632 | NULL, |
---|
1633 | (GClassInitFunc) gst_ximagesink_class_init, |
---|
1634 | NULL, |
---|
1635 | NULL, |
---|
1636 | sizeof (GstXImageSink), |
---|
1637 | 0, |
---|
1638 | (GInstanceInitFunc) gst_ximagesink_init, |
---|
1639 | }; |
---|
1640 | static const GInterfaceInfo iface_info = { |
---|
1641 | (GInterfaceInitFunc) gst_ximagesink_interface_init, |
---|
1642 | NULL, |
---|
1643 | NULL, |
---|
1644 | }; |
---|
1645 | static const GInterfaceInfo navigation_info = { |
---|
1646 | (GInterfaceInitFunc) gst_ximagesink_navigation_init, |
---|
1647 | NULL, |
---|
1648 | NULL, |
---|
1649 | }; |
---|
1650 | static const GInterfaceInfo overlay_info = { |
---|
1651 | (GInterfaceInitFunc) gst_ximagesink_xoverlay_init, |
---|
1652 | NULL, |
---|
1653 | NULL, |
---|
1654 | }; |
---|
1655 | |
---|
1656 | ximagesink_type = g_type_register_static (GST_TYPE_VIDEOSINK, |
---|
1657 | "GstXImageSink", &ximagesink_info, 0); |
---|
1658 | |
---|
1659 | g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE, |
---|
1660 | &iface_info); |
---|
1661 | g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION, |
---|
1662 | &navigation_info); |
---|
1663 | g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY, |
---|
1664 | &overlay_info); |
---|
1665 | } |
---|
1666 | |
---|
1667 | return ximagesink_type; |
---|
1668 | } |
---|
1669 | |
---|
1670 | static gboolean |
---|
1671 | plugin_init (GstPlugin * plugin) |
---|
1672 | { |
---|
1673 | /* Loading the library containing GstVideoSink, our parent object */ |
---|
1674 | if (!gst_library_load ("gstvideo")) |
---|
1675 | return FALSE; |
---|
1676 | |
---|
1677 | if (!gst_element_register (plugin, "ximagesink", |
---|
1678 | GST_RANK_SECONDARY, GST_TYPE_XIMAGESINK)) |
---|
1679 | return FALSE; |
---|
1680 | |
---|
1681 | GST_DEBUG_CATEGORY_INIT (gst_debug_ximagesink, "ximagesink", 0, |
---|
1682 | "ximagesink element"); |
---|
1683 | |
---|
1684 | return TRUE; |
---|
1685 | } |
---|
1686 | |
---|
1687 | GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
---|
1688 | GST_VERSION_MINOR, |
---|
1689 | "ximagesink", |
---|
1690 | "XFree86 video output plugin based on standard Xlib calls", |
---|
1691 | plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN) |
---|