source: trunk/third/gst-plugins/sys/xvimage/xvimagesink.c @ 21443

Revision 21443, 66.5 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#include <gst/colorbalance/colorbalance.h>
28
29/* Object header */
30#include "xvimagesink.h"
31
32/* Debugging category */
33#include <gst/gstinfo.h>
34GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
35#define GST_CAT_DEFAULT gst_debug_xvimagesink
36
37typedef struct
38{
39  unsigned long flags;
40  unsigned long functions;
41  unsigned long decorations;
42  long input_mode;
43  unsigned long status;
44}
45MotifWmHints, MwmHints;
46
47#define MWM_HINTS_DECORATIONS   (1L << 1)
48
49static void gst_xvimagesink_buffer_free (GstBuffer * buffer);
50static void gst_xvimagesink_xvimage_destroy (GstXvImageSink * xvimagesink,
51    GstXvImage * xvimage);
52
53
54/* ElementFactory information */
55static GstElementDetails gst_xvimagesink_details =
56GST_ELEMENT_DETAILS ("Video sink",
57    "Sink/Video",
58    "A Xv based videosink",
59    "Julien Moutte <julien@moutte.net>");
60
61/* Default template - initiated with class struct to allow gst-register to work
62   without X running */
63static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
64    GST_STATIC_PAD_TEMPLATE ("sink",
65    GST_PAD_SINK,
66    GST_PAD_ALWAYS,
67    GST_STATIC_CAPS ("video/x-raw-rgb, "
68        "framerate = (double) [ 1.0, 100.0 ], "
69        "width = (int) [ 1, MAX ], "
70        "height = (int) [ 1, MAX ]; "
71        "video/x-raw-yuv, "
72        "framerate = (double) [ 1.0, 100.0 ], "
73        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
74    );
75
76enum
77{
78  ARG_0,
79  ARG_CONTRAST,
80  ARG_BRIGHTNESS,
81  ARG_HUE,
82  ARG_SATURATION,
83  ARG_DISPLAY,
84  ARG_SYNCHRONOUS,
85  ARG_PIXEL_ASPECT_RATIO
86      /* FILL ME */
87};
88
89static GstVideoSinkClass *parent_class = NULL;
90static gboolean error_caught = FALSE;
91
92/* ============================================================= */
93/*                                                               */
94/*                       Private Methods                         */
95/*                                                               */
96/* ============================================================= */
97
98/* X11 stuff */
99
100static int
101gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
102{
103  char error_msg[1024];
104
105  XGetErrorText (display, xevent->error_code, error_msg, 1024);
106  GST_DEBUG ("xvimagesink failed to use XShm calls. error: %s", error_msg);
107  error_caught = TRUE;
108  return 0;
109}
110
111/* This function checks that it is actually really possible to create an image
112   using XShm */
113static gboolean
114gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
115{
116#ifndef HAVE_XSHM
117  return FALSE;
118#else
119  GstXvImage *xvimage = NULL;
120  int (*handler) (Display *, XErrorEvent *);
121  gboolean result = FALSE;
122
123  g_return_val_if_fail (xcontext != NULL, FALSE);
124
125  xvimage = g_new0 (GstXvImage, 1);
126  g_return_val_if_fail (xvimage != NULL, FALSE);
127
128  /* Setting an error handler to catch failure */
129  error_caught = FALSE;
130  handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
131
132  /* Trying to create a 1x1 picture */
133  GST_DEBUG ("XvShmCreateImage of 1x1");
134  xvimage->xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
135      xcontext->im_format, NULL, 1, 1, &xvimage->SHMInfo);
136  if (!xvimage->xvimage) {
137    GST_WARNING ("could not XvShmCreateImage a 1x1 image");
138    goto beach;
139  }
140  xvimage->size = xvimage->xvimage->data_size;
141
142  xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
143      IPC_CREAT | 0777);
144  if (xvimage->SHMInfo.shmid == -1) {
145    GST_WARNING ("could not get shared memory of %d bytes", xvimage->size);
146    goto beach;
147  }
148
149  xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
150  if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
151    GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
152    goto beach;
153  }
154
155  xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
156  xvimage->SHMInfo.readOnly = FALSE;
157
158  if (XShmAttach (xcontext->disp, &xvimage->SHMInfo) == 0) {
159    GST_WARNING ("Failed to XShmAttach");
160    goto beach;
161  }
162
163  XSync (xcontext->disp, 0);
164
165  XShmDetach (xcontext->disp, &xvimage->SHMInfo);
166  XSync (xcontext->disp, FALSE);
167
168  shmdt (xvimage->SHMInfo.shmaddr);
169  shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
170
171  /* To be sure, reset the SHMInfo entry */
172  xvimage->SHMInfo.shmaddr = ((void *) -1);
173
174  /* store whether we succeeded in result and reset error_caught */
175  result = !error_caught;
176  error_caught = FALSE;
177
178beach:
179  XSetErrorHandler (handler);
180  if (xvimage->xvimage)
181    XFree (xvimage->xvimage);
182  g_free (xvimage);
183  XSync (xcontext->disp, FALSE);
184  return result;
185#endif /* HAVE_XSHM */
186}
187
188/* This function handles GstXvImage creation depending on XShm availability */
189static GstXvImage *
190gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink,
191    gint width, gint height)
192{
193  GstXvImage *xvimage = NULL;
194  gboolean succeeded = FALSE;
195
196  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
197  GST_DEBUG_OBJECT (xvimagesink, "creating %dx%d", width, height);
198
199  xvimage = g_new0 (GstXvImage, 1);
200
201  xvimage->width = width;
202  xvimage->height = height;
203  xvimage->im_format = xvimagesink->xcontext->im_format;
204  xvimage->xvimagesink = xvimagesink;
205
206  g_mutex_lock (xvimagesink->x_lock);
207
208#ifdef HAVE_XSHM
209  if (xvimagesink->xcontext->use_xshm) {
210    xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
211        xvimagesink->xcontext->xv_port_id,
212        xvimage->im_format, NULL,
213        xvimage->width, xvimage->height, &xvimage->SHMInfo);
214    if (!xvimage->xvimage) {
215      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
216          ("could not XvShmCreateImage a %dx%d image"));
217      goto beach;
218    }
219
220    /* we have to use the returned data_size for our shm size */
221    xvimage->size = xvimage->xvimage->data_size;
222    GST_DEBUG_OBJECT (xvimagesink, "XShm image size is %d", xvimage->size);
223
224    xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
225        IPC_CREAT | 0777);
226    if (xvimage->SHMInfo.shmid == -1) {
227      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
228          ("could not get shared memory of %d bytes", xvimage->size));
229      goto beach;
230    }
231
232    xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
233    if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
234      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
235          ("Failed to shmat: %s", g_strerror (errno)));
236      goto beach;
237    }
238
239    xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
240    xvimage->SHMInfo.readOnly = FALSE;
241
242    if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
243      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
244          ("Failed to XShmAttach"));
245      goto beach;
246    }
247
248    XSync (xvimagesink->xcontext->disp, FALSE);
249  } else
250#endif /* HAVE_XSHM */
251  {
252    xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
253        xvimagesink->xcontext->xv_port_id,
254        xvimage->im_format, NULL, xvimage->width, xvimage->height);
255    if (!xvimage->xvimage) {
256      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
257          ("could not XvCreateImage a %dx%d image"));
258      goto beach;
259    }
260
261    /* we have to use the returned data_size for our image size */
262    xvimage->size = xvimage->xvimage->data_size;
263    xvimage->xvimage->data = g_malloc (xvimage->size);
264
265    XSync (xvimagesink->xcontext->disp, FALSE);
266  }
267  succeeded = TRUE;
268  g_mutex_unlock (xvimagesink->x_lock);
269
270beach:
271  if (!succeeded) {
272    gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage);
273    xvimage = NULL;
274  }
275
276  return xvimage;
277}
278
279/* This function destroys a GstXvImage handling XShm availability */
280static void
281gst_xvimagesink_xvimage_destroy (GstXvImageSink * xvimagesink,
282    GstXvImage * xvimage)
283{
284  g_return_if_fail (xvimage != NULL);
285  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
286
287  /* If the destroyed image is the current one we destroy our reference too */
288  if (xvimagesink->cur_image == xvimage)
289    xvimagesink->cur_image = NULL;
290
291  g_mutex_lock (xvimagesink->x_lock);
292
293#ifdef HAVE_XSHM
294  if (xvimagesink->xcontext->use_xshm) {
295    if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
296      XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
297      XSync (xvimagesink->xcontext->disp, FALSE);
298      shmdt (xvimage->SHMInfo.shmaddr);
299    }
300    if (xvimage->SHMInfo.shmid > 0)
301      shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
302    if (xvimage->xvimage)
303      XFree (xvimage->xvimage);
304  } else
305#endif /* HAVE_XSHM */
306  {
307    if (xvimage->xvimage) {
308      if (xvimage->xvimage->data) {
309        g_free (xvimage->xvimage->data);
310      }
311      XFree (xvimage->xvimage);
312    }
313  }
314
315  XSync (xvimagesink->xcontext->disp, FALSE);
316
317  g_mutex_unlock (xvimagesink->x_lock);
318
319  g_free (xvimage);
320}
321
322/* This function puts a GstXvImage on a GstXvImageSink's window */
323static void
324gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstXvImage * xvimage)
325{
326  g_return_if_fail (xvimage != NULL);
327  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
328
329  /* Store a reference to the last image we put */
330  if (xvimagesink->cur_image != xvimage)
331    xvimagesink->cur_image = xvimage;
332
333  g_mutex_lock (xvimagesink->x_lock);
334
335  /* We scale to the window's geometry */
336#ifdef HAVE_XSHM
337  if (xvimagesink->xcontext->use_xshm) {
338    GST_LOG_OBJECT (xvimagesink,
339        "XvShmPutImage with image %dx%d and window %dx%d",
340        xvimage->width, xvimage->height,
341        xvimagesink->xwindow->width, xvimagesink->xwindow->height);
342    XvShmPutImage (xvimagesink->xcontext->disp,
343        xvimagesink->xcontext->xv_port_id,
344        xvimagesink->xwindow->win,
345        xvimagesink->xwindow->gc, xvimage->xvimage,
346        0, 0, xvimage->width, xvimage->height,
347        0, 0, xvimagesink->xwindow->width, xvimagesink->xwindow->height, FALSE);
348  } else
349#endif /* HAVE_XSHM */
350  {
351    XvPutImage (xvimagesink->xcontext->disp,
352        xvimagesink->xcontext->xv_port_id,
353        xvimagesink->xwindow->win,
354        xvimagesink->xwindow->gc, xvimage->xvimage,
355        0, 0, xvimage->width, xvimage->height,
356        0, 0, xvimagesink->xwindow->width, xvimagesink->xwindow->height);
357  }
358
359  XSync (xvimagesink->xcontext->disp, FALSE);
360
361  g_mutex_unlock (xvimagesink->x_lock);
362}
363
364static gboolean
365gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
366    GstXWindow * window)
367{
368  Atom hints_atom = None;
369  MotifWmHints *hints;
370
371  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
372  g_return_val_if_fail (window != NULL, FALSE);
373
374  g_mutex_lock (xvimagesink->x_lock);
375
376  hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
377  if (hints_atom == None) {
378    g_mutex_unlock (xvimagesink->x_lock);
379    return FALSE;
380  }
381
382  hints = g_malloc0 (sizeof (MotifWmHints));
383
384  hints->flags |= MWM_HINTS_DECORATIONS;
385  hints->decorations = 1 << 0;
386
387  XChangeProperty (xvimagesink->xcontext->disp, window->win,
388      hints_atom, hints_atom, 32, PropModeReplace,
389      (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
390
391  XSync (xvimagesink->xcontext->disp, FALSE);
392
393  g_mutex_unlock (xvimagesink->x_lock);
394
395  g_free (hints);
396
397  return TRUE;
398}
399
400/* This function handles a GstXWindow creation
401 * The width and height are the actual pixel size on the display */
402static GstXWindow *
403gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
404    gint width, gint height)
405{
406  GstXWindow *xwindow = NULL;
407  XGCValues values;
408
409  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
410
411  xwindow = g_new0 (GstXWindow, 1);
412
413  xwindow->width = width;
414  xwindow->height = height;
415  xwindow->internal = TRUE;
416
417  g_mutex_lock (xvimagesink->x_lock);
418
419  xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
420      xvimagesink->xcontext->root,
421      0, 0, xwindow->width, xwindow->height,
422      0, 0, xvimagesink->xcontext->black);
423
424  XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
425      StructureNotifyMask | PointerMotionMask | KeyPressMask |
426      KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
427
428  xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
429      xwindow->win, 0, &values);
430
431  XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
432
433  XSync (xvimagesink->xcontext->disp, FALSE);
434
435  g_mutex_unlock (xvimagesink->x_lock);
436
437  gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
438
439  gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (xvimagesink), xwindow->win);
440
441  return xwindow;
442}
443
444/* This function destroys a GstXWindow */
445static void
446gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
447    GstXWindow * xwindow)
448{
449  g_return_if_fail (xwindow != NULL);
450  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
451
452  g_mutex_lock (xvimagesink->x_lock);
453
454  /* If we did not create that window we just free the GC and let it live */
455  if (xwindow->internal)
456    XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
457  else
458    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
459
460  XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
461
462  XSync (xvimagesink->xcontext->disp, FALSE);
463
464  g_mutex_unlock (xvimagesink->x_lock);
465
466  g_free (xwindow);
467}
468
469/* This function resizes a GstXWindow.
470 * The width and height are the actual pixel size on the display. */
471static void
472gst_xvimagesink_xwindow_resize (GstXvImageSink * xvimagesink,
473    GstXWindow * xwindow, guint width, guint height)
474{
475  g_return_if_fail (xwindow != NULL);
476  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
477
478  g_mutex_lock (xvimagesink->x_lock);
479
480  xwindow->width = width;
481  xwindow->height = height;
482
483  XResizeWindow (xvimagesink->xcontext->disp, xwindow->win,
484      xwindow->width, xwindow->height);
485
486  XSync (xvimagesink->xcontext->disp, FALSE);
487
488  g_mutex_unlock (xvimagesink->x_lock);
489}
490
491static void
492gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
493    GstXWindow * xwindow)
494{
495  g_return_if_fail (xwindow != NULL);
496  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
497
498  g_mutex_lock (xvimagesink->x_lock);
499
500  XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
501      xvimagesink->xcontext->black);
502
503  XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
504      0, 0, xwindow->width, xwindow->height);
505
506  XSync (xvimagesink->xcontext->disp, FALSE);
507
508  g_mutex_unlock (xvimagesink->x_lock);
509}
510
511/* This function commits our internal colorbalance settings to our grabbed Xv
512   port. If the xcontext is not initialized yet it simply returns */
513static void
514gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
515{
516  GList *channels = NULL;
517
518  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
519
520  /* If we haven't initialized the X context we can't update anything */
521  if (xvimagesink->xcontext == NULL)
522    return;
523
524  /* For each channel of the colorbalance we calculate the correct value
525     doing range conversion and then set the Xv port attribute to match our
526     values. */
527  channels = xvimagesink->xcontext->channels_list;
528
529  while (channels) {
530    if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
531      GstColorBalanceChannel *channel = NULL;
532      gint value = 0;
533      gdouble convert_coef;
534
535      channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
536      g_object_ref (channel);
537
538      /* Our range conversion coef */
539      convert_coef = (channel->max_value - channel->min_value) / 2000.0;
540
541      if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
542        value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
543      } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
544        value = (xvimagesink->saturation + 1000) * convert_coef +
545            channel->min_value;
546      } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
547        value = (xvimagesink->contrast + 1000) * convert_coef +
548            channel->min_value;
549      } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
550        value = (xvimagesink->brightness + 1000) * convert_coef +
551            channel->min_value;
552      } else {
553        g_warning ("got an unknown channel %s", channel->label);
554        g_object_unref (channel);
555        return;
556      }
557
558      /* Committing to Xv port */
559      g_mutex_lock (xvimagesink->x_lock);
560      XvSetPortAttribute (xvimagesink->xcontext->disp,
561          xvimagesink->xcontext->xv_port_id,
562          XInternAtom (xvimagesink->xcontext->disp, channel->label, 1), value);
563      g_mutex_unlock (xvimagesink->x_lock);
564
565      g_object_unref (channel);
566    }
567    channels = g_list_next (channels);
568  }
569}
570
571/* This function handles XEvents that might be in the queue. It generates
572   GstEvent that will be sent upstream in the pipeline to handle interactivity
573   and navigation. It will also listen for configure events on the window to
574   trigger caps renegotiation so on the fly software scaling can work. */
575static void
576gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink, GstPad * pad)
577{
578  XEvent e;
579  guint pointer_x = 0, pointer_y = 0;
580  gboolean pointer_moved = FALSE;
581
582  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
583
584  /* We get all pointer motion events, only the last position is
585     interesting. */
586  g_mutex_lock (xvimagesink->x_lock);
587  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
588          xvimagesink->xwindow->win, PointerMotionMask, &e)) {
589    g_mutex_unlock (xvimagesink->x_lock);
590
591    switch (e.type) {
592      case MotionNotify:
593        pointer_x = e.xmotion.x;
594        pointer_y = e.xmotion.y;
595        pointer_moved = TRUE;
596        break;
597      default:
598        break;
599    }
600
601    g_mutex_lock (xvimagesink->x_lock);
602  }
603  g_mutex_unlock (xvimagesink->x_lock);
604
605  if (pointer_moved) {
606    GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
607        pointer_x, pointer_y);
608    gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
609        "mouse-move", 0, pointer_x, pointer_y);
610  }
611
612  /* We get all events on our window to throw them upstream */
613  g_mutex_lock (xvimagesink->x_lock);
614  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
615          xvimagesink->xwindow->win,
616          StructureNotifyMask | KeyPressMask |
617          KeyReleaseMask | ButtonPressMask | ButtonReleaseMask, &e)) {
618    KeySym keysym;
619
620    /* We lock only for the X function call */
621    g_mutex_unlock (xvimagesink->x_lock);
622
623    switch (e.type) {
624      case ConfigureNotify:
625        /* Window got resized or moved. We update our data. */
626        GST_DEBUG ("xvimagesink window is at %d, %d with geometry : %d,%d",
627            e.xconfigure.x, e.xconfigure.y,
628            e.xconfigure.width, e.xconfigure.height);
629        xvimagesink->xwindow->width = e.xconfigure.width;
630        xvimagesink->xwindow->height = e.xconfigure.height;
631        break;
632      case ButtonPress:
633        /* Mouse button pressed over our window. We send upstream
634           events for interactivity/navigation */
635        GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
636            e.xbutton.button, e.xbutton.x, e.xbutton.y);
637        gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
638            "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
639        break;
640      case ButtonRelease:
641        /* Mouse button released over our window. We send upstream
642           events for interactivity/navigation */
643        GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
644            e.xbutton.button, e.xbutton.x, e.xbutton.y);
645        gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
646            "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
647        break;
648      case KeyPress:
649      case KeyRelease:
650        /* Key pressed/released over our window. We send upstream
651           events for interactivity/navigation */
652        GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
653            e.xkey.keycode, e.xkey.x, e.xkey.y);
654        keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
655            e.xkey.keycode, 0);
656        if (keysym != NoSymbol) {
657          gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
658              e.type == KeyPress ?
659              "key-press" : "key-release", XKeysymToString (keysym));
660        } else {
661          gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
662              e.type == KeyPress ? "key-press" : "key-release", "unknown");
663        }
664        break;
665      default:
666        GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
667    }
668
669    g_mutex_lock (xvimagesink->x_lock);
670  }
671  g_mutex_unlock (xvimagesink->x_lock);
672}
673
674/* This function generates a caps with all supported format by the first
675   Xv grabable port we find. We store each one of the supported formats in a
676   format list and append the format to a newly created caps that we return
677   If this function does not return NULL because of an error, it also grabs
678   the port via XvGrabPort */
679static GstCaps *
680gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
681    GstXContext * xcontext)
682{
683  gint i, nb_adaptors;
684  XvAdaptorInfo *adaptors;
685  gint nb_formats;
686  XvImageFormatValues *formats = NULL;
687  GstCaps *caps = NULL;
688
689  g_return_val_if_fail (xcontext != NULL, NULL);
690
691  /* First let's check that XVideo extension is available */
692  if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
693    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL),
694        ("XVideo extension is not available"));
695    return NULL;
696  }
697
698  /* Then we get adaptors list */
699  if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
700          &nb_adaptors, &adaptors)) {
701    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL),
702        ("Failed getting XV adaptors list"));
703    return NULL;
704  }
705
706  xcontext->xv_port_id = 0;
707
708  GST_DEBUG ("Found %d XV adaptor(s)", nb_adaptors);
709
710  /* Now search for an adaptor that supports XvImageMask */
711  for (i = 0; i < nb_adaptors && !xcontext->xv_port_id; i++) {
712    if (adaptors[i].type & XvImageMask) {
713      gint j;
714
715      /* We found such an adaptor, looking for an available port */
716      for (j = 0; j < adaptors[i].num_ports && !xcontext->xv_port_id; j++) {
717        /* We try to grab the port */
718        if (Success == XvGrabPort (xcontext->disp, adaptors[i].base_id + j, 0)) {
719          xcontext->xv_port_id = adaptors[i].base_id + j;
720        }
721      }
722    }
723
724    GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[i].name,
725        adaptors[i].num_ports);
726
727  }
728  XvFreeAdaptorInfo (adaptors);
729
730  if (!xcontext->xv_port_id) {
731    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY, (NULL),
732        ("No port available"));
733    return NULL;
734  }
735
736  /* Set XV_AUTOPAINT_COLORKEY */
737  {
738    int count;
739    XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
740        xcontext->xv_port_id, &count);
741    static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
742
743    for (i = 0; i < count; i++)
744      if (!strcmp (attr[i].name, autopaint)) {
745        const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
746
747        XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, 1);
748        break;
749      }
750
751    XFree (attr);
752  }
753
754  /* We get all image formats supported by our port */
755  formats = XvListImageFormats (xcontext->disp,
756      xcontext->xv_port_id, &nb_formats);
757  caps = gst_caps_new_empty ();
758  for (i = 0; i < nb_formats; i++) {
759    GstCaps *format_caps = NULL;
760
761    /* We set the image format of the xcontext to an existing one. Sink
762       connect method will override that but we need to have at least a
763       valid image format so that we can make our xshm calls check before
764       caps negotiation really happens. */
765    xcontext->im_format = formats[i].id;
766
767    switch (formats[i].type) {
768      case XvRGB:
769      {
770        format_caps = gst_caps_new_simple ("video/x-raw-rgb",
771            "endianness", G_TYPE_INT, xcontext->endianness,
772            "depth", G_TYPE_INT, xcontext->depth,
773            "bpp", G_TYPE_INT, xcontext->bpp,
774            "blue_mask", G_TYPE_INT, formats[i].red_mask,
775            "green_mask", G_TYPE_INT, formats[i].green_mask,
776            "red_mask", G_TYPE_INT, formats[i].blue_mask,
777            "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
778            "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
779            "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0, NULL);
780
781        /* For RGB caps we store them and the image
782           format so that we can get back the format
783           when sinkconnect will give us a caps without
784           format property */
785        if (format_caps) {
786          GstXvImageFormat *format = NULL;
787
788          format = g_new0 (GstXvImageFormat, 1);
789          if (format) {
790            format->format = formats[i].id;
791            format->caps = gst_caps_copy (format_caps);
792            xcontext->formats_list =
793                g_list_append (xcontext->formats_list, format);
794          }
795        }
796        break;
797      }
798      case XvYUV:
799        format_caps = gst_caps_new_simple ("video/x-raw-yuv",
800            "format", GST_TYPE_FOURCC, formats[i].id,
801            "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
802            "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
803            "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0, NULL);
804        break;
805      default:
806        g_assert_not_reached ();
807        break;
808    }
809
810    gst_caps_append (caps, format_caps);
811  }
812
813  if (formats)
814    XFree (formats);
815
816  GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
817
818  if (gst_caps_is_empty (caps)) {
819    gst_caps_free (caps);
820    XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
821    GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
822        ("No supported format found"));
823    return NULL;
824  }
825
826  return caps;
827}
828
829/* This function calculates the pixel aspect ratio based on the properties
830 * in the xcontext structure and stores it there. */
831static void
832gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
833{
834  gint par[][2] = {
835    {1, 1},                     /* regular screen */
836    {16, 15},                   /* PAL TV */
837    {11, 10},                   /* 525 line Rec.601 video */
838    {54, 59}                    /* 625 line Rec.601 video */
839  };
840  gint i;
841  gint index;
842  gdouble ratio;
843  gdouble delta;
844
845#define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
846
847  /* first calculate the "real" ratio based on the X values;
848   * which is the "physical" w/h divided by the w/h in pixels of the display */
849  ratio = (gdouble) (xcontext->widthmm * xcontext->height)
850      / (xcontext->heightmm * xcontext->width);
851
852  /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
853   * override here */
854  if (xcontext->width == 720 && xcontext->height == 576) {
855    ratio = 4.0 * 576 / (3.0 * 720);
856  }
857  GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
858  /* now find the one from par[][2] with the lowest delta to the real one */
859  delta = DELTA (0);
860  index = 0;
861
862  for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
863    gdouble this_delta = DELTA (i);
864
865    if (this_delta < delta) {
866      index = i;
867      delta = this_delta;
868    }
869  }
870
871  GST_DEBUG ("Decided on index %d (%d/%d)", index,
872      par[index][0], par[index][1]);
873
874  g_free (xcontext->par);
875  xcontext->par = g_new0 (GValue, 1);
876  g_value_init (xcontext->par, GST_TYPE_FRACTION);
877  gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
878  GST_DEBUG ("set xcontext PAR to %d/%d",
879      gst_value_get_fraction_numerator (xcontext->par),
880      gst_value_get_fraction_denominator (xcontext->par));
881}
882
883/* This function gets the X Display and global info about it. Everything is
884   stored in our object and will be cleaned when the object is disposed. Note
885   here that caps for supported format are generated without any window or
886   image creation */
887static GstXContext *
888gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
889{
890  GstXContext *xcontext = NULL;
891  XPixmapFormatValues *px_formats = NULL;
892  gint nb_formats = 0, i, j, N_attr;
893  XvAttribute *xv_attr;
894  char *channels[4] = { "XV_HUE", "XV_SATURATION",
895    "XV_BRIGHTNESS", "XV_CONTRAST"
896  };
897
898  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
899
900  xcontext = g_new0 (GstXContext, 1);
901  xcontext->im_format = 0;
902
903  g_mutex_lock (xvimagesink->x_lock);
904
905  xcontext->disp = XOpenDisplay (xvimagesink->display_name);
906
907  if (!xcontext->disp) {
908    g_mutex_unlock (xvimagesink->x_lock);
909    g_free (xcontext);
910    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
911        ("Could not open display"));
912    return NULL;
913  }
914
915  xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
916  xcontext->screen_num = DefaultScreen (xcontext->disp);
917  xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
918  xcontext->root = DefaultRootWindow (xcontext->disp);
919  xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
920  xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
921  xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
922
923  xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
924  xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
925  xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
926  xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
927
928  GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
929      xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
930
931
932  gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
933  /* We get supported pixmap formats at supported depth */
934  px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
935
936  if (!px_formats) {
937    XCloseDisplay (xcontext->disp);
938    g_mutex_unlock (xvimagesink->x_lock);
939    g_free (xcontext);
940    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL),
941        ("Could not get pixel formats"));
942    return NULL;
943  }
944
945  /* We get bpp value corresponding to our running depth */
946  for (i = 0; i < nb_formats; i++) {
947    if (px_formats[i].depth == xcontext->depth)
948      xcontext->bpp = px_formats[i].bits_per_pixel;
949  }
950
951  XFree (px_formats);
952
953  xcontext->endianness =
954      (ImageByteOrder (xcontext->disp) ==
955      LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
956
957  /* our caps system handles 24/32bpp RGB as big-endian. */
958  if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
959      xcontext->endianness == G_LITTLE_ENDIAN) {
960    xcontext->endianness = G_BIG_ENDIAN;
961    xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
962    xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
963    xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
964    if (xcontext->bpp == 24) {
965      xcontext->visual->red_mask >>= 8;
966      xcontext->visual->green_mask >>= 8;
967      xcontext->visual->blue_mask >>= 8;
968    }
969  }
970
971  xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
972
973  if (!xcontext->caps) {
974    XCloseDisplay (xcontext->disp);
975    g_mutex_unlock (xvimagesink->x_lock);
976    g_free (xcontext);
977    /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
978    return NULL;
979  }
980#ifdef HAVE_XSHM
981  /* Search for XShm extension support */
982  if (XShmQueryExtension (xcontext->disp) &&
983      gst_xvimagesink_check_xshm_calls (xcontext)) {
984    xcontext->use_xshm = TRUE;
985    GST_DEBUG ("xvimagesink is using XShm extension");
986  } else
987#endif /* HAVE_XSHM */
988  {
989    xcontext->use_xshm = FALSE;
990    GST_DEBUG ("xvimagesink is not using XShm extension");
991  }
992
993  xv_attr = XvQueryPortAttributes (xcontext->disp,
994      xcontext->xv_port_id, &N_attr);
995
996
997  /* Generate the channels list */
998  for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
999    XvAttribute *matching_attr = NULL;
1000
1001    if (xv_attr != NULL) {
1002      for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1003        if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1004          matching_attr = xv_attr + j;
1005    }
1006
1007    if (matching_attr) {
1008      GstColorBalanceChannel *channel;
1009
1010      channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1011      channel->label = g_strdup (channels[i]);
1012      channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1013      channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1014
1015      xcontext->channels_list = g_list_append (xcontext->channels_list,
1016          channel);
1017
1018      /* If the colorbalance settings have not been touched we get Xv values
1019         as defaults and update our internal variables */
1020      if (!xvimagesink->cb_changed) {
1021        gint val;
1022
1023        XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1024            XInternAtom (xcontext->disp, channel->label, 1), &val);
1025        /* Normalize val to [-1000, 1000] */
1026        val = -1000 + 2000 * (val - channel->min_value) /
1027            (channel->max_value - channel->min_value);
1028
1029        if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1030          xvimagesink->hue = val;
1031        else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1032          xvimagesink->saturation = val;
1033        else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1034          xvimagesink->brightness = val;
1035        else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1036          xvimagesink->contrast = val;
1037      }
1038    }
1039  }
1040
1041  if (xv_attr)
1042    XFree (xv_attr);
1043
1044  g_mutex_unlock (xvimagesink->x_lock);
1045
1046  return xcontext;
1047}
1048
1049/* This function cleans the X context. Closing the Display, releasing the XV
1050   port and unrefing the caps for supported formats. */
1051static void
1052gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1053{
1054  GList *formats_list, *channels_list;
1055
1056  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1057  g_return_if_fail (xvimagesink->xcontext != NULL);
1058
1059  formats_list = xvimagesink->xcontext->formats_list;
1060
1061  while (formats_list) {
1062    GstXvImageFormat *format = formats_list->data;
1063
1064    gst_caps_free (format->caps);
1065    g_free (format);
1066    formats_list = g_list_next (formats_list);
1067  }
1068
1069  if (xvimagesink->xcontext->formats_list)
1070    g_list_free (xvimagesink->xcontext->formats_list);
1071
1072  channels_list = xvimagesink->xcontext->channels_list;
1073
1074  while (channels_list) {
1075    GstColorBalanceChannel *channel = channels_list->data;
1076
1077    g_object_unref (channel);
1078    channels_list = g_list_next (channels_list);
1079  }
1080
1081  if (xvimagesink->xcontext->channels_list)
1082    g_list_free (xvimagesink->xcontext->channels_list);
1083
1084  gst_caps_free (xvimagesink->xcontext->caps);
1085  g_free (xvimagesink->xcontext->par);
1086
1087  g_mutex_lock (xvimagesink->x_lock);
1088
1089  XvUngrabPort (xvimagesink->xcontext->disp,
1090      xvimagesink->xcontext->xv_port_id, 0);
1091
1092  XCloseDisplay (xvimagesink->xcontext->disp);
1093
1094  g_mutex_unlock (xvimagesink->x_lock);
1095
1096  g_free (xvimagesink->xcontext);
1097  xvimagesink->xcontext = NULL;
1098}
1099
1100static void
1101gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
1102{
1103  g_mutex_lock (xvimagesink->pool_lock);
1104
1105  while (xvimagesink->image_pool) {
1106    GstXvImage *xvimage = xvimagesink->image_pool->data;
1107
1108    xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
1109        xvimagesink->image_pool);
1110    gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage);
1111  }
1112
1113  g_mutex_unlock (xvimagesink->pool_lock);
1114}
1115
1116/* Element stuff */
1117
1118static GstCaps *
1119gst_xvimagesink_fixate (GstPad * pad, const GstCaps * caps)
1120{
1121  GstStructure *structure;
1122  GstCaps *newcaps;
1123
1124  if (gst_caps_get_size (caps) > 1)
1125    return NULL;
1126
1127  newcaps = gst_caps_copy (caps);
1128  structure = gst_caps_get_structure (newcaps, 0);
1129
1130  if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 320)) {
1131    return newcaps;
1132  }
1133  if (gst_caps_structure_fixate_field_nearest_int (structure, "height", 240)) {
1134    return newcaps;
1135  }
1136  if (gst_caps_structure_fixate_field_nearest_double (structure, "framerate",
1137          30.0)) {
1138    return newcaps;
1139  }
1140
1141  gst_caps_free (newcaps);
1142  return NULL;
1143}
1144
1145/* This function tries to get a format matching with a given caps in the
1146   supported list of formats we generated in gst_xvimagesink_get_xv_support */
1147static gint
1148gst_xvimagesink_get_fourcc_from_caps (GstXvImageSink * xvimagesink,
1149    GstCaps * caps)
1150{
1151  GList *list = NULL;
1152
1153  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1154
1155  list = xvimagesink->xcontext->formats_list;
1156
1157  while (list) {
1158    GstXvImageFormat *format = list->data;
1159
1160    if (format) {
1161      GstCaps *icaps = NULL;
1162
1163      icaps = gst_caps_intersect (caps, format->caps);
1164      if (!gst_caps_is_empty (icaps))
1165        return format->format;
1166    }
1167    list = g_list_next (list);
1168  }
1169
1170  return 0;
1171}
1172
1173static GstCaps *
1174gst_xvimagesink_getcaps (GstPad * pad)
1175{
1176  GstXvImageSink *xvimagesink;
1177
1178  xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
1179
1180  if (xvimagesink->xcontext)
1181    return gst_caps_copy (xvimagesink->xcontext->caps);
1182
1183  return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1184}
1185
1186static GstPadLinkReturn
1187gst_xvimagesink_sink_link (GstPad * pad, const GstCaps * caps)
1188{
1189  GstXvImageSink *xvimagesink;
1190  GstStructure *structure;
1191  gint im_format = 0;
1192  gboolean ret;
1193  gint video_width, video_height;
1194  gint video_par_n, video_par_d;        /* video's PAR */
1195  gint display_par_n, display_par_d;    /* display's PAR */
1196  GValue display_ratio = { 0, };        /* display w/h ratio */
1197  const GValue *caps_par;
1198  gint num, den;
1199
1200  xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
1201
1202  GST_DEBUG_OBJECT (xvimagesink,
1203      "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1204      GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1205
1206  structure = gst_caps_get_structure (caps, 0);
1207  ret = gst_structure_get_int (structure, "width", &video_width);
1208  ret &= gst_structure_get_int (structure, "height", &video_height);
1209  ret &= gst_structure_get_double (structure, "framerate",
1210      &xvimagesink->framerate);
1211  if (!ret)
1212    return GST_PAD_LINK_REFUSED;
1213
1214  xvimagesink->video_width = video_width;
1215  xvimagesink->video_height = video_height;
1216  if (!gst_structure_get_fourcc (structure, "format", &im_format)) {
1217    im_format =
1218        gst_xvimagesink_get_fourcc_from_caps (xvimagesink,
1219        gst_caps_copy (caps));
1220  }
1221  if (im_format == 0) {
1222    return GST_PAD_LINK_REFUSED;
1223  }
1224
1225  /* get aspect ratio from caps if it's present, and
1226   * convert video width and height to a display width and height
1227   * using wd / hd = wv / hv * PARv / PARd
1228   * the ratio wd / hd will be stored in display_ratio */
1229  g_value_init (&display_ratio, GST_TYPE_FRACTION);
1230
1231  /* get video's PAR */
1232  caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1233  if (caps_par) {
1234    video_par_n = gst_value_get_fraction_numerator (caps_par);
1235    video_par_d = gst_value_get_fraction_denominator (caps_par);
1236  } else {
1237    video_par_n = 1;
1238    video_par_d = 1;
1239  }
1240  /* get display's PAR */
1241  if (xvimagesink->par) {
1242    display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1243    display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1244  } else {
1245    display_par_n = 1;
1246    display_par_d = 1;
1247  }
1248
1249  gst_value_set_fraction (&display_ratio,
1250      video_width * video_par_n * display_par_d,
1251      video_height * video_par_d * display_par_n);
1252
1253  num = gst_value_get_fraction_numerator (&display_ratio);
1254  den = gst_value_get_fraction_denominator (&display_ratio);
1255  GST_DEBUG_OBJECT (xvimagesink,
1256      "video width/height: %dx%d, calculated display ratio: %d/%d",
1257      video_width, video_height, num, den);
1258
1259  /* now find a width x height that respects this display ratio.
1260   * prefer those that have one of w/h the same as the incoming video
1261   * using wd / hd = num / den */
1262
1263  /* start with same height, because of interlaced video */
1264  /* check hd / den is an integer scale factor, and scale wd with the PAR */
1265  if (video_height % den == 0) {
1266    GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1267    GST_VIDEOSINK_WIDTH (xvimagesink) = video_height * num / den;
1268    GST_VIDEOSINK_HEIGHT (xvimagesink) = video_height;
1269  } else if (video_width % num == 0) {
1270    GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1271    GST_VIDEOSINK_WIDTH (xvimagesink) = video_width;
1272    GST_VIDEOSINK_HEIGHT (xvimagesink) = video_width * den / num;
1273  } else {
1274    GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1275    GST_VIDEOSINK_WIDTH (xvimagesink) = video_height * num / den;
1276    GST_VIDEOSINK_HEIGHT (xvimagesink) = video_height;
1277  }
1278  GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1279      GST_VIDEOSINK_WIDTH (xvimagesink), GST_VIDEOSINK_HEIGHT (xvimagesink));
1280
1281  /* Creating our window and our image with the display size in pixels */
1282  g_assert (GST_VIDEOSINK_WIDTH (xvimagesink) > 0);
1283  g_assert (GST_VIDEOSINK_HEIGHT (xvimagesink) > 0);
1284  if (!xvimagesink->xwindow)
1285    xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1286        GST_VIDEOSINK_WIDTH (xvimagesink), GST_VIDEOSINK_HEIGHT (xvimagesink));
1287  else {
1288    if (xvimagesink->xwindow->internal)
1289      gst_xvimagesink_xwindow_resize (xvimagesink, xvimagesink->xwindow,
1290          GST_VIDEOSINK_WIDTH (xvimagesink),
1291          GST_VIDEOSINK_HEIGHT (xvimagesink));
1292  }
1293
1294  /* We renew our xvimage only if size or format changed;
1295   * the xvimage is the same size as the video pixel size */
1296  if ((xvimagesink->xvimage) &&
1297      ((im_format != xvimagesink->xvimage->im_format) ||
1298          (video_width != xvimagesink->xvimage->width) ||
1299          (video_height != xvimagesink->xvimage->height))) {
1300    GST_DEBUG_OBJECT (xvimagesink,
1301        "old format " GST_FOURCC_FORMAT ", new format " GST_FOURCC_FORMAT,
1302        GST_FOURCC_ARGS (xvimagesink->xcontext->im_format),
1303        GST_FOURCC_ARGS (im_format));
1304    GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
1305    gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage);
1306    xvimagesink->xvimage = NULL;
1307  }
1308
1309  xvimagesink->xcontext->im_format = im_format;
1310
1311  gst_x_overlay_got_desired_size (GST_X_OVERLAY (xvimagesink),
1312      GST_VIDEOSINK_WIDTH (xvimagesink), GST_VIDEOSINK_HEIGHT (xvimagesink));
1313
1314  return GST_PAD_LINK_OK;
1315}
1316
1317static GstElementStateReturn
1318gst_xvimagesink_change_state (GstElement * element)
1319{
1320  GstXvImageSink *xvimagesink;
1321
1322  xvimagesink = GST_XVIMAGESINK (element);
1323
1324  switch (GST_STATE_TRANSITION (element)) {
1325    case GST_STATE_NULL_TO_READY:
1326      /* Initializing the XContext */
1327      if (!xvimagesink->xcontext &&
1328          !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink)))
1329        return GST_STATE_FAILURE;
1330      /* update object's par with calculated one if not set yet */
1331      if (!xvimagesink->par) {
1332        xvimagesink->par = g_new0 (GValue, 1);
1333        gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1334        GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1335      }
1336      /* call XSynchronize with the current value of synchronous */
1337      GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1338          xvimagesink->synchronous ? "TRUE" : "FALSE");
1339      XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1340      gst_xvimagesink_update_colorbalance (xvimagesink);
1341      break;
1342    case GST_STATE_READY_TO_PAUSED:
1343      if (xvimagesink->xwindow)
1344        gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
1345      xvimagesink->time = 0;
1346      break;
1347    case GST_STATE_PAUSED_TO_PLAYING:
1348      break;
1349    case GST_STATE_PLAYING_TO_PAUSED:
1350      break;
1351    case GST_STATE_PAUSED_TO_READY:
1352      xvimagesink->framerate = 0;
1353      GST_VIDEOSINK_WIDTH (xvimagesink) = 0;
1354      GST_VIDEOSINK_HEIGHT (xvimagesink) = 0;
1355      break;
1356    case GST_STATE_READY_TO_NULL:
1357      if (xvimagesink->xvimage) {
1358        gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage);
1359        xvimagesink->xvimage = NULL;
1360      }
1361
1362      if (xvimagesink->image_pool)
1363        gst_xvimagesink_imagepool_clear (xvimagesink);
1364
1365      if (xvimagesink->xwindow) {
1366        gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1367        xvimagesink->xwindow = NULL;
1368      }
1369
1370      if (xvimagesink->xcontext) {
1371        gst_xvimagesink_xcontext_clear (xvimagesink);
1372        xvimagesink->xcontext = NULL;
1373      }
1374      break;
1375  }
1376
1377  if (GST_ELEMENT_CLASS (parent_class)->change_state)
1378    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1379
1380  return GST_STATE_SUCCESS;
1381}
1382
1383static void
1384gst_xvimagesink_chain (GstPad * pad, GstData * data)
1385{
1386  GstBuffer *buf = NULL;
1387  GstXvImageSink *xvimagesink;
1388
1389  g_return_if_fail (GST_IS_PAD (pad));
1390  g_return_if_fail (data != NULL);
1391
1392  xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
1393
1394  if (GST_IS_EVENT (data)) {
1395    gst_pad_event_default (pad, GST_EVENT (data));
1396    return;
1397  }
1398
1399  buf = GST_BUFFER (data);
1400  /* update time */
1401  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1402    xvimagesink->time = GST_BUFFER_TIMESTAMP (buf);
1403  }
1404  GST_LOG_OBJECT (xvimagesink, "clock wait: %" GST_TIME_FORMAT,
1405      GST_TIME_ARGS (xvimagesink->time));
1406
1407  if (GST_VIDEOSINK_CLOCK (xvimagesink)) {
1408    gst_element_wait (GST_ELEMENT (xvimagesink), xvimagesink->time);
1409  }
1410
1411  /* If this buffer has been allocated using our buffer management we simply
1412     put the ximage which is in the PRIVATE pointer */
1413  if (GST_BUFFER_FREE_DATA_FUNC (buf) == gst_xvimagesink_buffer_free) {
1414    gst_xvimagesink_xvimage_put (xvimagesink, GST_BUFFER_PRIVATE (buf));
1415  } else {
1416    /* Else we have to copy the data into our private image, */
1417    /* if we have one... */
1418    if (!xvimagesink->xvimage) {
1419      GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
1420      xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
1421          xvimagesink->video_width, xvimagesink->video_height);
1422      if (!xvimagesink->xvimage) {
1423        /* No image available. That's very bad ! */
1424        gst_buffer_unref (buf);
1425        GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1426            ("Failed creating an XvImage in xvimagesink chain function."));
1427        return;
1428      }
1429    }
1430
1431    memcpy (xvimagesink->xvimage->xvimage->data,
1432        GST_BUFFER_DATA (buf),
1433        MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
1434    gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
1435  }
1436
1437  /* set correct time for next buffer */
1438  if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf) && xvimagesink->framerate > 0) {
1439    xvimagesink->time += GST_SECOND / xvimagesink->framerate;
1440  }
1441
1442  gst_buffer_unref (buf);
1443
1444  gst_xvimagesink_handle_xevents (xvimagesink, pad);
1445}
1446
1447/* Buffer management */
1448
1449static void
1450gst_xvimagesink_buffer_free (GstBuffer * buffer)
1451{
1452  GstXvImageSink *xvimagesink;
1453  GstXvImage *xvimage;
1454
1455  xvimage = GST_BUFFER_PRIVATE (buffer);
1456
1457  g_assert (GST_IS_XVIMAGESINK (xvimage->xvimagesink));
1458  xvimagesink = xvimage->xvimagesink;
1459
1460  /* If our geometry changed we can't reuse that image. */
1461  if ((xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) ||
1462      (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink)))
1463    gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage);
1464  else {
1465    /* In that case we can reuse the image and add it to our image pool. */
1466    g_mutex_lock (xvimagesink->pool_lock);
1467    xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
1468        xvimage);
1469    g_mutex_unlock (xvimagesink->pool_lock);
1470  }
1471}
1472
1473static GstBuffer *
1474gst_xvimagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size)
1475{
1476  GstXvImageSink *xvimagesink;
1477  GstBuffer *buffer;
1478  GstXvImage *xvimage = NULL;
1479  gboolean not_found = TRUE;
1480
1481  xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
1482
1483  g_mutex_lock (xvimagesink->pool_lock);
1484
1485  /* Walking through the pool cleaning unusable images and searching for a
1486     suitable one */
1487  while (not_found && xvimagesink->image_pool) {
1488    xvimage = xvimagesink->image_pool->data;
1489
1490    if (xvimage) {
1491      /* Removing from the pool */
1492      xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
1493          xvimagesink->image_pool);
1494
1495      /* We check for geometry or image format changes */
1496      if ((xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) ||
1497          (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink)) ||
1498          (xvimage->im_format != xvimagesink->xcontext->im_format)) {
1499        /* This image is unusable. Destroying... */
1500        gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage);
1501        xvimage = NULL;
1502      } else {
1503        /* We found a suitable image */
1504        break;
1505      }
1506    }
1507  }
1508
1509  g_mutex_unlock (xvimagesink->pool_lock);
1510
1511  if (!xvimage) {
1512    /* We found no suitable image in the pool. Creating... */
1513    GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
1514    xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
1515        xvimagesink->video_width, xvimagesink->video_height);
1516  }
1517
1518  if (xvimage) {
1519    buffer = gst_buffer_new ();
1520
1521    /* Storing some pointers in the buffer */
1522    GST_BUFFER_PRIVATE (buffer) = xvimage;
1523
1524    GST_BUFFER_DATA (buffer) = xvimage->xvimage->data;
1525    GST_BUFFER_FREE_DATA_FUNC (buffer) = gst_xvimagesink_buffer_free;
1526    GST_BUFFER_SIZE (buffer) = xvimage->size;
1527    return buffer;
1528  } else
1529    return NULL;
1530}
1531
1532/* Interfaces stuff */
1533
1534static gboolean
1535gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
1536{
1537  g_assert (type == GST_TYPE_NAVIGATION ||
1538      type == GST_TYPE_X_OVERLAY || type == GST_TYPE_COLOR_BALANCE);
1539  return TRUE;
1540}
1541
1542static void
1543gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
1544{
1545  klass->supported = gst_xvimagesink_interface_supported;
1546}
1547
1548static void
1549gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1550    GstStructure * structure)
1551{
1552  GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1553  GstEvent *event;
1554  double x, y;
1555
1556  event = gst_event_new (GST_EVENT_NAVIGATION);
1557  event->event_data.structure.structure = structure;
1558
1559  /* Converting pointer coordinates to the non scaled geometry */
1560  if (gst_structure_get_double (structure, "pointer_x", &x)) {
1561    x *= GST_VIDEOSINK_WIDTH (xvimagesink);
1562    x /= xvimagesink->xwindow->width;
1563    gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1564  }
1565  if (gst_structure_get_double (structure, "pointer_y", &y)) {
1566    y *= GST_VIDEOSINK_HEIGHT (xvimagesink);
1567    y /= xvimagesink->xwindow->height;
1568    gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1569  }
1570
1571  gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD (xvimagesink)),
1572      event);
1573}
1574
1575static void
1576gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1577{
1578  iface->send_event = gst_xvimagesink_navigation_send_event;
1579}
1580
1581static void
1582gst_xvimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1583{
1584  GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1585  GstXWindow *xwindow = NULL;
1586  XWindowAttributes attr;
1587
1588  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1589
1590  /* If we already use that window return */
1591  if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win))
1592    return;
1593
1594  /* If the element has not initialized the X11 context try to do so */
1595  if (!xvimagesink->xcontext &&
1596      !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink)))
1597    /* we have thrown a GST_ELEMENT_ERROR now */
1598    return;
1599
1600  gst_xvimagesink_update_colorbalance (xvimagesink);
1601
1602  /* Clear image pool as the images are unusable anyway */
1603  gst_xvimagesink_imagepool_clear (xvimagesink);
1604
1605  /* Clear the xvimage */
1606  if (xvimagesink->xvimage) {
1607    gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage);
1608    xvimagesink->xvimage = NULL;
1609  }
1610
1611  /* If a window is there already we destroy it */
1612  if (xvimagesink->xwindow) {
1613    gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1614    xvimagesink->xwindow = NULL;
1615  }
1616
1617  /* If the xid is 0 we go back to an internal window */
1618  if (xwindow_id == 0) {
1619    /* If no width/height caps nego did not happen window will be created
1620       during caps nego then */
1621    if (GST_VIDEOSINK_WIDTH (xvimagesink) && GST_VIDEOSINK_HEIGHT (xvimagesink)) {
1622      xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1623          GST_VIDEOSINK_WIDTH (xvimagesink),
1624          GST_VIDEOSINK_HEIGHT (xvimagesink));
1625    }
1626  } else {
1627    xwindow = g_new0 (GstXWindow, 1);
1628
1629    xwindow->win = xwindow_id;
1630
1631    /* We get window geometry, set the event we want to receive,
1632       and create a GC */
1633    g_mutex_lock (xvimagesink->x_lock);
1634    XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
1635    xwindow->width = attr.width;
1636    xwindow->height = attr.height;
1637    xwindow->internal = FALSE;
1638    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
1639        StructureNotifyMask | PointerMotionMask | KeyPressMask |
1640        KeyReleaseMask);
1641
1642    xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1643        xwindow->win, 0, NULL);
1644    g_mutex_unlock (xvimagesink->x_lock);
1645  }
1646
1647  if (xwindow)
1648    xvimagesink->xwindow = xwindow;
1649}
1650
1651static void
1652gst_xvimagesink_get_desired_size (GstXOverlay * overlay,
1653    guint * width, guint * height)
1654{
1655  GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1656
1657  *width = GST_VIDEOSINK_WIDTH (xvimagesink);
1658  *height = GST_VIDEOSINK_HEIGHT (xvimagesink);
1659}
1660
1661static void
1662gst_xvimagesink_expose (GstXOverlay * overlay)
1663{
1664  XWindowAttributes attr;
1665  GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1666
1667  if (!xvimagesink->xwindow)
1668    return;
1669
1670  /* Update the window geometry */
1671  g_mutex_lock (xvimagesink->x_lock);
1672  XGetWindowAttributes (xvimagesink->xcontext->disp,
1673      xvimagesink->xwindow->win, &attr);
1674  g_mutex_unlock (xvimagesink->x_lock);
1675
1676  xvimagesink->xwindow->width = attr.width;
1677  xvimagesink->xwindow->height = attr.height;
1678
1679  if (xvimagesink->cur_image) {
1680    gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->cur_image);
1681  }
1682}
1683
1684static void
1685gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
1686{
1687  iface->set_xwindow_id = gst_xvimagesink_set_xwindow_id;
1688  iface->get_desired_size = gst_xvimagesink_get_desired_size;
1689  iface->expose = gst_xvimagesink_expose;
1690}
1691
1692static const GList *
1693gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1694{
1695  GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1696
1697  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1698
1699  if (xvimagesink->xcontext)
1700    return xvimagesink->xcontext->channels_list;
1701  else
1702    return NULL;
1703}
1704
1705static void
1706gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1707    GstColorBalanceChannel * channel, gint value)
1708{
1709  GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1710
1711  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1712  g_return_if_fail (channel->label != NULL);
1713
1714  xvimagesink->cb_changed = TRUE;
1715
1716  /* Normalize val to [-1000, 1000] */
1717  value = -1000 + 2000 * (value - channel->min_value) /
1718      (channel->max_value - channel->min_value);
1719
1720  if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1721    xvimagesink->hue = value;
1722  } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1723    xvimagesink->saturation = value;
1724  } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1725    xvimagesink->contrast = value;
1726  } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1727    xvimagesink->brightness = value;
1728  } else {
1729    g_warning ("got an unknown channel %s", channel->label);
1730    return;
1731  }
1732
1733  gst_xvimagesink_update_colorbalance (xvimagesink);
1734}
1735
1736static gint
1737gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1738    GstColorBalanceChannel * channel)
1739{
1740  GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1741  gint value = 0;
1742
1743  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1744  g_return_val_if_fail (channel->label != NULL, 0);
1745
1746  if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1747    value = xvimagesink->hue;
1748  } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1749    value = xvimagesink->saturation;
1750  } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1751    value = xvimagesink->contrast;
1752  } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1753    value = xvimagesink->brightness;
1754  } else {
1755    g_warning ("got an unknown channel %s", channel->label);
1756  }
1757
1758  /* Normalize val to [channel->min_value, channel->max_value] */
1759  value = channel->min_value + (channel->max_value - channel->min_value) *
1760      (value + 1000) / 2000;
1761
1762  return value;
1763}
1764
1765static void
1766gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
1767{
1768  GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
1769  iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1770  iface->set_value = gst_xvimagesink_colorbalance_set_value;
1771  iface->get_value = gst_xvimagesink_colorbalance_get_value;
1772}
1773
1774/* =========================================== */
1775/*                                             */
1776/*              Init & Class init              */
1777/*                                             */
1778/* =========================================== */
1779
1780static void
1781gst_xvimagesink_set_property (GObject * object, guint prop_id,
1782    const GValue * value, GParamSpec * pspec)
1783{
1784  GstXvImageSink *xvimagesink;
1785
1786  g_return_if_fail (GST_IS_XVIMAGESINK (object));
1787
1788  xvimagesink = GST_XVIMAGESINK (object);
1789
1790  switch (prop_id) {
1791    case ARG_HUE:
1792      xvimagesink->hue = g_value_get_int (value);
1793      xvimagesink->cb_changed = TRUE;
1794      gst_xvimagesink_update_colorbalance (xvimagesink);
1795      break;
1796    case ARG_CONTRAST:
1797      xvimagesink->contrast = g_value_get_int (value);
1798      xvimagesink->cb_changed = TRUE;
1799      gst_xvimagesink_update_colorbalance (xvimagesink);
1800      break;
1801    case ARG_BRIGHTNESS:
1802      xvimagesink->brightness = g_value_get_int (value);
1803      xvimagesink->cb_changed = TRUE;
1804      gst_xvimagesink_update_colorbalance (xvimagesink);
1805      break;
1806    case ARG_SATURATION:
1807      xvimagesink->saturation = g_value_get_int (value);
1808      xvimagesink->cb_changed = TRUE;
1809      gst_xvimagesink_update_colorbalance (xvimagesink);
1810      break;
1811    case ARG_DISPLAY:
1812      xvimagesink->display_name = g_strdup (g_value_get_string (value));
1813      break;
1814    case ARG_SYNCHRONOUS:
1815      xvimagesink->synchronous = g_value_get_boolean (value);
1816      if (xvimagesink->xcontext) {
1817        XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1818        GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1819            xvimagesink->synchronous ? "TRUE" : "FALSE");
1820      }
1821      break;
1822    case ARG_PIXEL_ASPECT_RATIO:
1823      g_free (xvimagesink->par);
1824      xvimagesink->par = g_new0 (GValue, 1);
1825      g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1826      if (!g_value_transform (value, xvimagesink->par)) {
1827        g_warning ("Could not transform string to aspect ratio");
1828        gst_value_set_fraction (xvimagesink->par, 1, 1);
1829      }
1830      GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1831          gst_value_get_fraction_numerator (xvimagesink->par),
1832          gst_value_get_fraction_denominator (xvimagesink->par));
1833      break;
1834    default:
1835      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1836      break;
1837  }
1838}
1839
1840static void
1841gst_xvimagesink_get_property (GObject * object, guint prop_id,
1842    GValue * value, GParamSpec * pspec)
1843{
1844  GstXvImageSink *xvimagesink;
1845
1846  g_return_if_fail (GST_IS_XVIMAGESINK (object));
1847
1848  xvimagesink = GST_XVIMAGESINK (object);
1849
1850  switch (prop_id) {
1851    case ARG_HUE:
1852      g_value_set_int (value, xvimagesink->hue);
1853      break;
1854    case ARG_CONTRAST:
1855      g_value_set_int (value, xvimagesink->contrast);
1856      break;
1857    case ARG_BRIGHTNESS:
1858      g_value_set_int (value, xvimagesink->brightness);
1859      break;
1860    case ARG_SATURATION:
1861      g_value_set_int (value, xvimagesink->saturation);
1862      break;
1863    case ARG_DISPLAY:
1864      g_value_set_string (value, g_strdup (xvimagesink->display_name));
1865      break;
1866    case ARG_SYNCHRONOUS:
1867      g_value_set_boolean (value, xvimagesink->synchronous);
1868      break;
1869    case ARG_PIXEL_ASPECT_RATIO:
1870      if (xvimagesink->par)
1871        g_value_transform (xvimagesink->par, value);
1872      break;
1873    default:
1874      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1875      break;
1876  }
1877}
1878
1879/* Finalize is called only once, dispose can be called multiple times.
1880 * We use mutexes and don't reset stuff to NULL here so let's register
1881 * as a finalize. */
1882static void
1883gst_xvimagesink_finalize (GObject * object)
1884{
1885  GstXvImageSink *xvimagesink;
1886
1887  xvimagesink = GST_XVIMAGESINK (object);
1888
1889  if (xvimagesink->display_name) {
1890    g_free (xvimagesink->display_name);
1891    xvimagesink->display_name = NULL;
1892  }
1893
1894  if (xvimagesink->par) {
1895    g_free (xvimagesink->par);
1896    xvimagesink->par = NULL;
1897  }
1898  if (xvimagesink->x_lock) {
1899    g_mutex_free (xvimagesink->x_lock);
1900    xvimagesink->x_lock = NULL;
1901  }
1902  if (xvimagesink->pool_lock) {
1903    g_mutex_free (xvimagesink->pool_lock);
1904    xvimagesink->pool_lock = NULL;
1905  }
1906
1907  G_OBJECT_CLASS (parent_class)->finalize (object);
1908}
1909
1910static void
1911gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1912{
1913  GST_VIDEOSINK_PAD (xvimagesink) =
1914      gst_pad_new_from_template (gst_static_pad_template_get
1915      (&gst_xvimagesink_sink_template_factory), "sink");
1916
1917  gst_element_add_pad (GST_ELEMENT (xvimagesink),
1918      GST_VIDEOSINK_PAD (xvimagesink));
1919
1920  gst_pad_set_chain_function (GST_VIDEOSINK_PAD (xvimagesink),
1921      gst_xvimagesink_chain);
1922  gst_pad_set_link_function (GST_VIDEOSINK_PAD (xvimagesink),
1923      gst_xvimagesink_sink_link);
1924  gst_pad_set_getcaps_function (GST_VIDEOSINK_PAD (xvimagesink),
1925      gst_xvimagesink_getcaps);
1926  gst_pad_set_fixate_function (GST_VIDEOSINK_PAD (xvimagesink),
1927      gst_xvimagesink_fixate);
1928  gst_pad_set_bufferalloc_function (GST_VIDEOSINK_PAD (xvimagesink),
1929      gst_xvimagesink_buffer_alloc);
1930
1931  xvimagesink->display_name = NULL;
1932  xvimagesink->xcontext = NULL;
1933  xvimagesink->xwindow = NULL;
1934  xvimagesink->xvimage = NULL;
1935  xvimagesink->cur_image = NULL;
1936
1937  xvimagesink->hue = xvimagesink->saturation = 0;
1938  xvimagesink->contrast = xvimagesink->brightness = 0;
1939  xvimagesink->cb_changed = FALSE;
1940
1941  xvimagesink->framerate = 0;
1942  xvimagesink->video_width = 0;
1943  xvimagesink->video_height = 0;
1944
1945  xvimagesink->x_lock = g_mutex_new ();
1946
1947  xvimagesink->image_pool = NULL;
1948  xvimagesink->pool_lock = g_mutex_new ();
1949
1950  xvimagesink->synchronous = FALSE;
1951  xvimagesink->par = NULL;
1952
1953  GST_FLAG_SET (xvimagesink, GST_ELEMENT_THREAD_SUGGESTED);
1954  GST_FLAG_SET (xvimagesink, GST_ELEMENT_EVENT_AWARE);
1955}
1956
1957static void
1958gst_xvimagesink_base_init (gpointer g_class)
1959{
1960  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1961
1962  gst_element_class_set_details (element_class, &gst_xvimagesink_details);
1963
1964  gst_element_class_add_pad_template (element_class,
1965      gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
1966}
1967
1968static void
1969gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1970{
1971  GObjectClass *gobject_class;
1972  GstElementClass *gstelement_class;
1973
1974  gobject_class = (GObjectClass *) klass;
1975  gstelement_class = (GstElementClass *) klass;
1976
1977  parent_class = g_type_class_ref (GST_TYPE_VIDEOSINK);
1978
1979  g_object_class_install_property (gobject_class, ARG_CONTRAST,
1980      g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1981          -1000, 1000, 0, G_PARAM_READWRITE));
1982  g_object_class_install_property (gobject_class, ARG_BRIGHTNESS,
1983      g_param_spec_int ("brightness", "Brightness",
1984          "The brightness of the video", -1000, 1000, 0, G_PARAM_READWRITE));
1985  g_object_class_install_property (gobject_class, ARG_HUE,
1986      g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1987          G_PARAM_READWRITE));
1988  g_object_class_install_property (gobject_class, ARG_SATURATION,
1989      g_param_spec_int ("saturation", "Saturation",
1990          "The saturation of the video", -1000, 1000, 0, G_PARAM_READWRITE));
1991  g_object_class_install_property (gobject_class, ARG_DISPLAY,
1992      g_param_spec_string ("display", "Display", "X Display name", NULL,
1993          G_PARAM_READWRITE));
1994  g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS,
1995      g_param_spec_boolean ("synchronous", "Synchronous",
1996          "When enabled, runs "
1997          "the X display in synchronous mode. (used only for debugging)", FALSE,
1998          G_PARAM_READWRITE));
1999  g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO,
2000      g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2001          "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2002
2003  gobject_class->finalize = gst_xvimagesink_finalize;
2004  gobject_class->set_property = gst_xvimagesink_set_property;
2005  gobject_class->get_property = gst_xvimagesink_get_property;
2006
2007  gstelement_class->change_state = gst_xvimagesink_change_state;
2008}
2009
2010/* ============================================================= */
2011/*                                                               */
2012/*                       Public Methods                          */
2013/*                                                               */
2014/* ============================================================= */
2015
2016/* =========================================== */
2017/*                                             */
2018/*          Object typing & Creation           */
2019/*                                             */
2020/* =========================================== */
2021
2022GType
2023gst_xvimagesink_get_type (void)
2024{
2025  static GType xvimagesink_type = 0;
2026
2027  if (!xvimagesink_type) {
2028    static const GTypeInfo xvimagesink_info = {
2029      sizeof (GstXvImageSinkClass),
2030      gst_xvimagesink_base_init,
2031      NULL,
2032      (GClassInitFunc) gst_xvimagesink_class_init,
2033      NULL,
2034      NULL,
2035      sizeof (GstXvImageSink),
2036      0,
2037      (GInstanceInitFunc) gst_xvimagesink_init,
2038    };
2039    static const GInterfaceInfo iface_info = {
2040      (GInterfaceInitFunc) gst_xvimagesink_interface_init,
2041      NULL,
2042      NULL,
2043    };
2044    static const GInterfaceInfo navigation_info = {
2045      (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
2046      NULL,
2047      NULL,
2048    };
2049    static const GInterfaceInfo overlay_info = {
2050      (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
2051      NULL,
2052      NULL,
2053    };
2054    static const GInterfaceInfo colorbalance_info = {
2055      (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
2056      NULL,
2057      NULL,
2058    };
2059
2060    xvimagesink_type = g_type_register_static (GST_TYPE_VIDEOSINK,
2061        "GstXvImageSink", &xvimagesink_info, 0);
2062
2063    g_type_add_interface_static (xvimagesink_type,
2064        GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
2065    g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
2066        &navigation_info);
2067    g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY,
2068        &overlay_info);
2069    g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE,
2070        &colorbalance_info);
2071  }
2072
2073  return xvimagesink_type;
2074}
2075
2076static gboolean
2077plugin_init (GstPlugin * plugin)
2078{
2079  /* Loading the library containing GstVideoSink, our parent object */
2080  if (!gst_library_load ("gstvideo"))
2081    return FALSE;
2082
2083  if (!gst_element_register (plugin, "xvimagesink",
2084          GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
2085    return FALSE;
2086
2087  GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
2088      "xvimagesink element");
2089
2090  return TRUE;
2091}
2092
2093GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2094    GST_VERSION_MINOR,
2095    "xvimagesink",
2096    "XFree86 video output plugin using Xv extension",
2097    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)
Note: See TracBrowser for help on using the repository browser.