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