source: trunk/third/gst-plugins/sys/ximage/ximagesink.c @ 21443

Revision 21443, 52.6 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21442, which included commits to RCS files with non-trunk default branches.
Line 
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>
33GST_DEBUG_CATEGORY_STATIC (gst_debug_ximagesink);
34#define GST_CAT_DEFAULT gst_debug_ximagesink
35
36typedef struct
37{
38  unsigned long flags;
39  unsigned long functions;
40  unsigned long decorations;
41  long input_mode;
42  unsigned long status;
43}
44MotifWmHints, MwmHints;
45
46#define MWM_HINTS_DECORATIONS   (1L << 1)
47
48static void gst_ximagesink_buffer_free (GstBuffer * buffer);
49static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
50    GstXImage * ximage);
51
52/* ElementFactory information */
53static GstElementDetails gst_ximagesink_details =
54GST_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 */
61static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
62GST_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
70enum
71{
72  ARG_0,
73  ARG_DISPLAY,
74  ARG_SYNCHRONOUS,
75  ARG_PIXEL_ASPECT_RATIO
76      /* FILL ME */
77};
78
79static GstVideoSinkClass *parent_class = NULL;
80static gboolean error_caught = FALSE;
81
82/* ============================================================= */
83/*                                                               */
84/*                       Private Methods                         */
85/*                                                               */
86/* ============================================================= */
87
88/* X11 stuff */
89
90static int
91gst_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 */
103static gboolean
104gst_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
168beach:
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 */
179static GstXImage *
180gst_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
261beach:
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 */
271static void
272gst_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 */
311static void
312gst_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
351static gboolean
352gst_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 */
388static GstXWindow *
389gst_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 */
427static void
428gst_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 */
452static void
453gst_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
472static void
473gst_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
491static void
492gst_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
510static void
511gst_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. */
585static void
586gst_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. */
677static void
678gst_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 */
734static GstXContext *
735gst_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. */
855static void
856gst_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
876static void
877gst_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
894static GstCaps *
895gst_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
923static GstCaps *
924gst_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
952static GstPadLinkReturn
953gst_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
1017static GstElementStateReturn
1018gst_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
1078static void
1079gst_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
1146static void
1147gst_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
1169static GstBuffer *
1170gst_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
1228static gboolean
1229gst_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
1235static void
1236gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1237{
1238  klass->supported = gst_ximagesink_interface_supported;
1239}
1240
1241static void
1242gst_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
1274static void
1275gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1276{
1277  iface->send_event = gst_ximagesink_navigation_send_event;
1278}
1279
1280static void
1281gst_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
1387static void
1388gst_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
1397static void
1398gst_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
1418static void
1419gst_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
1432static void
1433gst_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
1472static void
1473gst_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
1499static void
1500gst_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
1527static void
1528gst_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
1571static void
1572gst_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
1582static void
1583gst_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
1623GType
1624gst_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
1670static gboolean
1671plugin_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
1687GST_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)
Note: See TracBrowser for help on using the repository browser.