ext/directfb/dfbvideosink.c: When finalizing GstDfbSurface, a subclass of GstBuffer...
[platform/upstream/gstreamer.git] / ext / directfb / dfbvideosink.c
1 /* GStreamer DirectFB plugin
2  * Copyright (C) 2005 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 /**
21  * SECTION:element-dfbvideosink
22  *
23  * <refsect2>
24  * <para>
25  * DfbVideoSink renders video frames using the
26  * <ulink url="http://www.directfb.org/">DirectFB</ulink> library.
27  * Rendering can happen in two different modes :
28  * <itemizedlist>
29  * <listitem>
30  *   <para>
31  *   Standalone: this mode will take complete control of the monitor forcing
32  *   <ulink url="http://www.directfb.org/">DirectFB</ulink> to fullscreen layout.
33  *   This is convenient to test using the  gst-launch command line tool or
34  *   other simple applications. It is possible to interrupt playback while
35  *   being in this mode by pressing the Escape key.
36  *   </para>
37  *   <para>
38  *   This mode handles navigation events for every input device supported by
39  *   the <ulink url="http://www.directfb.org/">DirectFB</ulink> library, it will
40  *   look for available video modes in the fb.modes file and try to switch
41  *   the framebuffer video mode to the most suitable one. Depending on 
42  *   hardware acceleration capabilities the element will handle scaling or not.
43  *   If no acceleration is available it will do clipping or centering of the
44  *   video frames respecting the original aspect ratio.
45  *   </para>
46  * </listitem>
47  * <listitem>
48  *   <para>
49  *   Embedded: this mode will render video frames in a 
50  *   <link linkend="GstDfbVideoSink--surface">surface</link> provided by the
51  *   application developer. This is a more advanced usage of the element and
52  *   it is required to integrate video playback in existing 
53  *   <ulink url="http://www.directfb.org/">DirectFB</ulink> applications.
54  *   </para>
55  *   <para>
56  *   When using this mode the element just renders to the
57  *   <link linkend="GstDfbVideoSink--surface">surface</link> provided by the 
58  *   application, that means it won't handle navigation events and won't resize
59  *   the <link linkend="GstDfbVideoSink--surface">surface</link> to fit video
60  *   frames geometry. Application has to implement the necessary code to grab
61  *   informations about the negotiated geometry and resize there
62  *   <link linkend="GstDfbVideoSink--surface">surface</link> accordingly.
63  *   </para>
64  * </listitem>
65  * </itemizedlist>
66  * For both modes the element implements a buffer pool allocation system to 
67  * optimize memory allocation time and handle reverse negotiation. Indeed if 
68  * you insert an element like videoscale in the pipeline the video sink will
69  * negotiate with it to try get a scaled video for either the fullscreen layout
70  * or the application provided external
71  * <link linkend="GstDfbVideoSink--surface">surface</link>.
72  * </para>
73  * <title>Example application</title>
74  * <para>
75  * <include xmlns="http://www.w3.org/2003/XInclude" href="element-dfb-example.xml" />
76  * </para>
77  * <title>Example pipelines</title>
78  * <para>
79  * Here is a test pipeline to test the colorbalance interface :
80  * <programlisting>
81  * gst-launch -v videotestsrc ! dfbvideosink hue=20000 saturation=40000 brightness=25000
82  * </programlisting>
83  * </para>
84  * </refsect2>
85  */
86
87 #ifdef HAVE_CONFIG_H
88 #include "config.h"
89 #endif
90
91 /* Our interfaces */
92 #include <gst/interfaces/navigation.h>
93 #include <gst/interfaces/colorbalance.h>
94
95 /* Object header */
96 #include "dfbvideosink.h"
97
98 #include <string.h>
99
100 /* Debugging category */
101 GST_DEBUG_CATEGORY_STATIC (dfbvideosink_debug);
102 #define GST_CAT_DEFAULT dfbvideosink_debug
103
104 /* ElementFactory information */
105 static const GstElementDetails gst_dfbvideosink_details =
106 GST_ELEMENT_DETAILS ("DirectFB video sink",
107     "Sink/Video",
108     "A DirectFB based videosink",
109     "Julien Moutte <julien@moutte.net>");
110
111 /* Default template */
112 static GstStaticPadTemplate gst_dfbvideosink_sink_template_factory =
113     GST_STATIC_PAD_TEMPLATE ("sink",
114     GST_PAD_SINK,
115     GST_PAD_ALWAYS,
116     GST_STATIC_CAPS ("video/x-raw-rgb, "
117         "framerate = (fraction) [ 0, MAX ], "
118         "width = (int) [ 1, MAX ], "
119         "height = (int) [ 1, MAX ]; "
120         "video/x-raw-yuv, "
121         "framerate = (fraction) [ 0, MAX ], "
122         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
123     );
124
125 /* Signals and args */
126 enum
127 {
128   ARG_0,
129   ARG_SURFACE,
130   ARG_CONTRAST,
131   ARG_BRIGHTNESS,
132   ARG_HUE,
133   ARG_SATURATION,
134   ARG_PIXEL_ASPECT_RATIO,
135   ARG_VSYNC
136 };
137
138 static void gst_dfbvideosink_bufferpool_clear (GstDfbVideoSink * dfbvideosink);
139 static DFBSurfacePixelFormat gst_dfbvideosink_get_format_from_caps (GstCaps *
140     caps);
141 static void gst_dfbvideosink_update_colorbalance (GstDfbVideoSink *
142     dfbvideosink);
143 static void gst_dfbvideosink_surface_destroy (GstDfbVideoSink * dfbvideosink,
144     GstDfbSurface * surface);
145
146 static GstVideoSinkClass *parent_class = NULL;
147 static GstBufferClass *surface_parent_class = NULL;
148
149 static const char *
150 gst_dfbvideosink_get_format_name (DFBSurfacePixelFormat format)
151 {
152   switch (format) {
153     case DSPF_ARGB1555:
154       return "ARGB1555";
155     case DSPF_RGB16:
156       return "RGB16";
157     case DSPF_RGB24:
158       return "RGB24";
159     case DSPF_RGB32:
160       return "RGB32";
161     case DSPF_ARGB:
162       return "ARGB";
163     case DSPF_A8:
164       return "A8";
165     case DSPF_YUY2:
166       return "YUY2";
167     case DSPF_RGB332:
168       return "RGB33";
169     case DSPF_UYVY:
170       return "UYVY";
171     case DSPF_I420:
172       return "I420";
173     case DSPF_YV12:
174       return "YV12";
175     case DSPF_LUT8:
176       return "LUT8";
177     case DSPF_ALUT44:
178       return "ALUT44";
179     case DSPF_AiRGB:
180       return "AiRGB";
181     case DSPF_A1:
182       return "A1";
183     case DSPF_NV12:
184       return "NV12";
185     case DSPF_NV16:
186       return "NV16";
187     case DSPF_ARGB2554:
188       return "ARGB2554";
189     case DSPF_ARGB4444:
190       return "ARGB4444";
191     case DSPF_NV21:
192       return "NV21";
193     default:
194       return "UNKNOWN";
195   }
196 }
197
198 /* Creates miniobject and our internal surface */
199 static GstDfbSurface *
200 gst_dfbvideosink_surface_create (GstDfbVideoSink * dfbvideosink, GstCaps * caps,
201     size_t size)
202 {
203   GstDfbSurface *surface = NULL;
204   GstStructure *structure = NULL;
205   DFBResult ret;
206   DFBSurfaceDescription s_dsc;
207   gpointer data;
208   gint pitch;
209   gboolean succeeded = FALSE;
210
211   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), NULL);
212
213   surface = (GstDfbSurface *) gst_mini_object_new (GST_TYPE_DFBSURFACE);
214
215   /* Keep a ref to our sink */
216   surface->dfbvideosink = gst_object_ref (dfbvideosink);
217   /* Surface is not locked yet */
218   surface->locked = FALSE;
219
220   structure = gst_caps_get_structure (caps, 0);
221
222   if (!gst_structure_get_int (structure, "width", &surface->width) ||
223       !gst_structure_get_int (structure, "height", &surface->height)) {
224     GST_WARNING_OBJECT (dfbvideosink, "failed getting geometry from caps %"
225         GST_PTR_FORMAT, caps);
226     goto fallback;
227   }
228
229   /* Pixel format from caps */
230   surface->pixel_format = gst_dfbvideosink_get_format_from_caps (caps);
231   if (surface->pixel_format == DSPF_UNKNOWN) {
232     goto fallback;
233   }
234
235   if (!dfbvideosink->dfb) {
236     GST_DEBUG_OBJECT (dfbvideosink, "no DirectFB context to create a surface");
237     goto fallback;
238   }
239
240   /* Creating an internal surface which will be used as GstBuffer, we used
241      the detected pixel format and video dimensions */
242
243   s_dsc.flags =
244       DSDESC_PIXELFORMAT | DSDESC_WIDTH | DSDESC_HEIGHT /*| DSDESC_CAPS */ ;
245
246   s_dsc.pixelformat = surface->pixel_format;
247   s_dsc.width = surface->width;
248   s_dsc.height = surface->height;
249   /*s_dsc.caps = DSCAPS_VIDEOONLY; */
250
251   ret = dfbvideosink->dfb->CreateSurface (dfbvideosink->dfb, &s_dsc,
252       &surface->surface);
253   if (ret != DFB_OK) {
254     GST_WARNING_OBJECT (dfbvideosink, "failed creating a DirectFB surface");
255     surface->surface = NULL;
256     goto fallback;
257   }
258
259   /* Clearing surface */
260   surface->surface->Clear (surface->surface, 0x00, 0x00, 0x00, 0xFF);
261
262   /* Locking the surface to acquire the memory pointer */
263   surface->surface->Lock (surface->surface, DSLF_WRITE, &data, &pitch);
264   surface->locked = TRUE;
265   GST_BUFFER_DATA (surface) = data;
266   GST_BUFFER_SIZE (surface) = pitch * surface->height;
267
268   /* Be carefull here. If size is different from the surface size
269      (pitch * height), we can't use that surface through buffer alloc system
270      or we are going to run into serious stride issues */
271   if (GST_BUFFER_SIZE (surface) != size) {
272     GST_WARNING_OBJECT (dfbvideosink, "DirectFB surface size (%dx%d=%d) "
273         "differs from GStreamer requested size %d", pitch, surface->height,
274         GST_BUFFER_SIZE (surface), size);
275     goto fallback;
276   }
277
278   GST_DEBUG_OBJECT (dfbvideosink, "creating a %dx%d surface (%p) with %s "
279       "pixel format, line pitch %d", surface->width, surface->height, surface,
280       gst_dfbvideosink_get_format_name (surface->pixel_format), pitch);
281
282   succeeded = TRUE;
283
284   goto beach;
285
286 fallback:
287
288   /* We allocate a standard buffer ourselves to store it in our buffer pool,
289      this is an optimisation for memory allocation */
290   GST_BUFFER (surface)->malloc_data = g_malloc (size);
291   GST_BUFFER_DATA (surface) = GST_BUFFER (surface)->malloc_data;
292   GST_BUFFER_SIZE (surface) = size;
293   if (surface->surface) {
294     if (surface->locked) {
295       surface->surface->Unlock (surface->surface);
296       surface->locked = FALSE;
297     }
298     surface->surface->Release (surface->surface);
299     surface->surface = NULL;
300   }
301   GST_DEBUG_OBJECT (dfbvideosink, "allocating a buffer (%p) of %d bytes",
302       surface, size);
303
304   succeeded = TRUE;
305
306 beach:
307   if (!succeeded) {
308     gst_dfbvideosink_surface_destroy (dfbvideosink, surface);
309     surface = NULL;
310   }
311   return surface;
312 }
313
314 /* We are called from the finalize method of miniobject, the object will be
315  * destroyed so we just have to clean our internal stuff */
316 static void
317 gst_dfbvideosink_surface_destroy (GstDfbVideoSink * dfbvideosink,
318     GstDfbSurface * surface)
319 {
320   g_return_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink));
321
322   /* Release our internal surface */
323   if (surface->surface) {
324     if (surface->locked) {
325       surface->surface->Unlock (surface->surface);
326       surface->locked = FALSE;
327     }
328     surface->surface->Release (surface->surface);
329     surface->surface = NULL;
330   }
331
332   if (surface->dfbvideosink) {
333     /* Release the ref to our sink */
334     surface->dfbvideosink = NULL;
335     gst_object_unref (dfbvideosink);
336   }
337
338   GST_MINI_OBJECT_CLASS (surface_parent_class)->
339       finalize (GST_MINI_OBJECT (surface));
340 }
341
342 static gpointer
343 gst_dfbvideosink_event_thread (GstDfbVideoSink * dfbvideosink)
344 {
345   DFBResult ret;
346
347   while (dfbvideosink->running) {
348     /* Wait for an event with a 50 ms timeout */
349     dfbvideosink->event_buffer->WaitForEventWithTimeout (dfbvideosink->
350         event_buffer, 0, 50);
351
352     /* Do we have an event ? */
353     ret = dfbvideosink->event_buffer->HasEvent (dfbvideosink->event_buffer);
354
355     if (ret == DFB_OK) {
356       DFBEvent event;
357
358       GST_DEBUG_OBJECT (dfbvideosink, "we have an event");
359
360       ret = dfbvideosink->event_buffer->GetEvent (dfbvideosink->event_buffer,
361           &event);
362       if (ret != DFB_OK) {      /* Error */
363         GST_WARNING_OBJECT (dfbvideosink, "failed when getting event from "
364             "event buffer");
365       } else {                  /* Handle event */
366         if (event.input.type == DIET_KEYPRESS) {
367           switch (event.input.key_symbol) {
368             case DIKS_ESCAPE:
369             {
370               GST_ELEMENT_ERROR (dfbvideosink, RESOURCE, OPEN_WRITE,
371                   ("Video output device is gone."),
372                   ("We were running fullscreen and user "
373                       "pressed the ESC key, stopping playback."));
374             }
375             default:
376               GST_DEBUG_OBJECT (dfbvideosink, "key press event %c !",
377                   event.input.key_symbol);
378               gst_navigation_send_key_event (GST_NAVIGATION (dfbvideosink),
379                   "key-press", "prout");
380           }
381         } else if (event.input.type == DIET_BUTTONPRESS) {
382           gint x, y;
383
384           dfbvideosink->layer->GetCursorPosition (dfbvideosink->layer, &x, &y);
385
386           GST_DEBUG_OBJECT (dfbvideosink, "button %d pressed at %dx%d",
387               event.input.button, x, y);
388
389           gst_navigation_send_mouse_event (GST_NAVIGATION (dfbvideosink),
390               "mouse-button-press", event.input.button, x, y);
391         } else if (event.input.type == DIET_BUTTONRELEASE) {
392           gint x, y;
393
394           dfbvideosink->layer->GetCursorPosition (dfbvideosink->layer, &x, &y);
395
396           GST_DEBUG_OBJECT (dfbvideosink, "button %d released at %dx%d",
397               event.input.button, x, y);
398
399           gst_navigation_send_mouse_event (GST_NAVIGATION (dfbvideosink),
400               "mouse-button-release", event.input.button, x, y);
401         } else if (event.input.type == DIET_AXISMOTION) {
402           gint x, y;
403
404           dfbvideosink->layer->GetCursorPosition (dfbvideosink->layer, &x, &y);
405           gst_navigation_send_mouse_event (GST_NAVIGATION (dfbvideosink),
406               "mouse-move", 0, x, y);
407         } else {
408           GST_WARNING_OBJECT (dfbvideosink, "unhandled event type %d",
409               event.input.type);
410         }
411       }
412     }
413   }
414   return NULL;
415 }
416
417 static DFBEnumerationResult
418 gst_dfbvideosink_enum_layers (DFBDisplayLayerID id,
419     DFBDisplayLayerDescription desc, void *data)
420 {
421   GstDfbVideoSink *dfbvideosink = NULL;
422   IDirectFBDisplayLayer *layer = NULL;
423   DFBDisplayLayerConfig dlc;
424   DFBResult ret;
425   gboolean backbuffer = FALSE;
426
427   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (data), DFENUM_CANCEL);
428
429   dfbvideosink = GST_DFBVIDEOSINK (data);
430
431   GST_DEBUG_OBJECT (dfbvideosink, "inspecting display layer %d with name: %s",
432       id, desc.name);
433
434   if ((desc.type & DLTF_VIDEO) && (desc.caps & DLCAPS_SURFACE)) {
435     GST_DEBUG_OBJECT (dfbvideosink,
436         "this layer can handle live video and has a surface");
437   } else {
438     if (desc.caps & DLCAPS_SURFACE) {
439       GST_DEBUG_OBJECT (dfbvideosink,
440           "this layer can not handle live video but has a surface");
441     } else {
442       GST_DEBUG_OBJECT (dfbvideosink, "no we can't use that layer, really...");
443       goto beach;
444     }
445   }
446
447   ret = dfbvideosink->dfb->GetDisplayLayer (dfbvideosink->dfb, id, &layer);
448   if (ret != DFB_OK) {
449     GST_WARNING_OBJECT (dfbvideosink, "failed getting display layer %s",
450         desc.name);
451     goto beach;
452   }
453
454   ret = layer->GetConfiguration (layer, &dlc);
455   if (ret != DFB_OK) {
456     GST_WARNING_OBJECT (dfbvideosink,
457         "failed getting display layer configuration");
458     goto beach;
459   }
460
461   if ((dlc.flags & DLCONF_BUFFERMODE) && (dlc.buffermode & DLBM_FRONTONLY)) {
462     GST_DEBUG_OBJECT (dfbvideosink, "no backbuffer");
463   }
464   if ((dlc.flags & DLCONF_BUFFERMODE) && (dlc.buffermode & DLBM_BACKVIDEO)) {
465     GST_DEBUG_OBJECT (dfbvideosink, "backbuffer is in video memory");
466     backbuffer = TRUE;
467   }
468   if ((dlc.flags & DLCONF_BUFFERMODE) && (dlc.buffermode & DLBM_BACKSYSTEM)) {
469     GST_DEBUG_OBJECT (dfbvideosink, "backbuffer is in system memory");
470     backbuffer = TRUE;
471   }
472   if ((dlc.flags & DLCONF_BUFFERMODE) && (dlc.buffermode & DLBM_TRIPLE)) {
473     GST_DEBUG_OBJECT (dfbvideosink, "triple buffering");
474     backbuffer = TRUE;
475   }
476
477   /* If the primary is suitable we prefer using it */
478   if (dfbvideosink->layer_id != DLID_PRIMARY) {
479     GST_DEBUG_OBJECT (dfbvideosink, "selecting layer named %s", desc.name);
480     dfbvideosink->layer_id = id;
481     dfbvideosink->backbuffer = backbuffer;
482   } else {
483     GST_DEBUG_OBJECT (dfbvideosink, "layer %s is suitable but the primary "
484         "is currently selected and we prefer that one", desc.name);
485   }
486
487 beach:
488   if (layer) {
489     layer->Release (layer);
490   }
491   return DFENUM_OK;
492 }
493
494 static DFBEnumerationResult
495 gst_dfbvideosink_enum_vmodes (gint width, gint height, gint bpp, void *data)
496 {
497   GstDfbVideoSink *dfbvideosink = NULL;
498   GstDfbVMode *vmode = NULL;
499
500   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (data), DFENUM_CANCEL);
501
502   dfbvideosink = GST_DFBVIDEOSINK (data);
503
504   GST_DEBUG_OBJECT (dfbvideosink, "adding video mode %dx%d at %d bpp", width,
505       height, bpp);
506   vmode = g_new0 (GstDfbVMode, 1);
507   vmode->width = width;
508   vmode->height = height;
509   vmode->bpp = bpp;
510
511   /* We need to know the maximum video geometry we can accept for the caps */
512   if (width > dfbvideosink->out_width) {
513     dfbvideosink->out_width = width;
514   }
515   if (height > dfbvideosink->out_height) {
516     dfbvideosink->out_height = height;
517   }
518
519   dfbvideosink->vmodes = g_slist_append (dfbvideosink->vmodes, vmode);
520
521   return DFENUM_OK;
522 }
523
524 static DFBEnumerationResult
525 gst_dfbvideosink_enum_devices (DFBInputDeviceID id,
526     DFBInputDeviceDescription desc, void *data)
527 {
528   GstDfbVideoSink *dfbvideosink = NULL;
529   IDirectFBInputDevice *device = NULL;
530   DFBResult ret;
531
532   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (data), DFENUM_CANCEL);
533
534   dfbvideosink = GST_DFBVIDEOSINK (data);
535
536   GST_DEBUG_OBJECT (dfbvideosink, "detected input device %s from vendor %s",
537       desc.name, desc.vendor);
538
539   /* Get that input device */
540   ret = dfbvideosink->dfb->GetInputDevice (dfbvideosink->dfb, id, &device);
541   if (ret != DFB_OK) {
542     GST_WARNING_OBJECT (dfbvideosink, "failed when getting input device id %d",
543         id);
544     goto beach;
545   }
546
547   ret = device->AttachEventBuffer (device, dfbvideosink->event_buffer);
548   if (ret != DFB_OK) {
549     GST_WARNING_OBJECT (dfbvideosink, "failed when attaching input device "
550         "%d to our event buffer", id);
551   }
552
553 beach:
554   if (device) {
555     device->Release (device);
556   }
557   return DFENUM_OK;
558 }
559
560 static gboolean
561 gst_dfbvideosink_setup (GstDfbVideoSink * dfbvideosink)
562 {
563   DFBResult ret;
564
565   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), FALSE);
566
567   dfbvideosink->video_width = 0;
568   dfbvideosink->video_height = 0;
569   dfbvideosink->out_width = 0;
570   dfbvideosink->out_height = 0;
571   dfbvideosink->fps_d = 0;
572   dfbvideosink->fps_n = 0;
573   dfbvideosink->hw_scaling = FALSE;
574   dfbvideosink->backbuffer = FALSE;
575   dfbvideosink->pixel_format = DSPF_UNKNOWN;
576
577   /* If we do it all by ourself we create the DirectFB context, get the 
578      primary layer and use a fullscreen configuration */
579   if (!dfbvideosink->ext_surface) {
580     GST_DEBUG_OBJECT (dfbvideosink, "no external surface, taking over "
581         "DirectFB fullscreen");
582     if (!dfbvideosink->dfb) {
583       DFBGraphicsDeviceDescription hw_caps;
584
585       GST_DEBUG_OBJECT (dfbvideosink, "initializing DirectFB");
586
587       ret = DirectFBInit (0, NULL);
588
589       if (ret != DFB_OK) {
590         GST_WARNING_OBJECT (dfbvideosink, "DirectFB initialization failed");
591         goto beach;
592       }
593
594       ret = DirectFBCreate (&(dfbvideosink->dfb));
595
596       if (ret != DFB_OK) {
597         GST_WARNING_OBJECT (dfbvideosink, "failed creating the DirectFB "
598             "main object");
599         goto beach;
600       }
601
602       /* Get Hardware capabilities */
603       ret = dfbvideosink->dfb->GetDeviceDescription (dfbvideosink->dfb,
604           &hw_caps);
605
606       if (ret != DFB_OK) {
607         GST_WARNING_OBJECT (dfbvideosink, "failed grabbing the hardware "
608             "capabilities");
609         goto beach;
610       }
611
612       GST_DEBUG_OBJECT (dfbvideosink, "video card %s from vendor %s detected "
613           "with %d bytes of video memory", hw_caps.name, hw_caps.vendor,
614           hw_caps.video_memory);
615
616       if (hw_caps.acceleration_mask & DFXL_BLIT) {
617         GST_DEBUG_OBJECT (dfbvideosink, "Blit is accelerated");
618       }
619       if (hw_caps.acceleration_mask & DFXL_STRETCHBLIT) {
620         GST_DEBUG_OBJECT (dfbvideosink, "StretchBlit is accelerated");
621         dfbvideosink->hw_scaling = TRUE;
622       } else {
623         GST_DEBUG_OBJECT (dfbvideosink, "StretchBlit is not accelerated");
624         dfbvideosink->hw_scaling = FALSE;
625       }
626
627       dfbvideosink->layer_id = -1;
628
629       /* Inspect all the Display layers */
630       dfbvideosink->dfb->EnumDisplayLayers (dfbvideosink->dfb,
631           gst_dfbvideosink_enum_layers, dfbvideosink);
632       /* Inspect all Video modes */
633       dfbvideosink->dfb->EnumVideoModes (dfbvideosink->dfb,
634           gst_dfbvideosink_enum_vmodes, dfbvideosink);
635
636       /* Create an event buffer for input */
637       dfbvideosink->dfb->CreateEventBuffer (dfbvideosink->dfb,
638           &dfbvideosink->event_buffer);
639
640       /* Inspect all Input devices */
641       dfbvideosink->dfb->EnumInputDevices (dfbvideosink->dfb,
642           gst_dfbvideosink_enum_devices, dfbvideosink);
643       /* Create a thread to handle those events */
644       dfbvideosink->event_thread = g_thread_create (
645           (GThreadFunc) gst_dfbvideosink_event_thread,
646           dfbvideosink, TRUE, NULL);
647     }
648     if (!dfbvideosink->layer) {
649       GList *channels_list = NULL;
650       DFBDisplayLayerDescription dl_desc;
651
652       /* Get the best Display Layer */
653       ret = dfbvideosink->dfb->GetDisplayLayer (dfbvideosink->dfb,
654           dfbvideosink->layer_id, &dfbvideosink->layer);
655       if (ret != DFB_OK) {
656         GST_WARNING_OBJECT (dfbvideosink, "failed getting display layer");
657         goto beach;
658       }
659
660       ret = dfbvideosink->layer->SetCooperativeLevel (dfbvideosink->layer,
661           DLSCL_EXCLUSIVE);
662
663       if (ret != DFB_OK) {
664         GST_WARNING_OBJECT (dfbvideosink, "failed setting display layer to "
665             "fullscreen mode");
666         goto beach;
667       }
668
669       dfbvideosink->layer->GetDescription (dfbvideosink->layer, &dl_desc);
670
671       /* Check that this layer is able to do colorbalance settings */
672       if (dl_desc.caps & DLCAPS_BRIGHTNESS) {
673         channels_list = g_list_append (channels_list, "BRIGHTNESS");
674       }
675       if (dl_desc.caps & DLCAPS_CONTRAST) {
676         channels_list = g_list_append (channels_list, "CONTRAST");
677       }
678       if (dl_desc.caps & DLCAPS_HUE) {
679         channels_list = g_list_append (channels_list, "HUE");
680       }
681       if (dl_desc.caps & DLCAPS_SATURATION) {
682         channels_list = g_list_append (channels_list, "SATURATION");
683       }
684
685       if (channels_list) {
686         GList *walk = channels_list;
687
688         /* Generate Color balance channel list */
689         while (walk) {
690           GstColorBalanceChannel *channel = NULL;
691
692           GST_DEBUG_OBJECT (dfbvideosink, "adding %s as a colorbalance channel",
693               walk->data);
694
695           channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
696           channel->label = g_strdup (walk->data);
697           channel->min_value = 0x0000;
698           channel->max_value = 0xFFFF;
699
700           dfbvideosink->cb_channels = g_list_append (dfbvideosink->cb_channels,
701               channel);
702
703           walk = g_list_next (walk);
704         }
705
706         /* If the colorbalance settings have not been touched we get current
707            values as defaults and update our internal variables */
708         if (!dfbvideosink->cb_changed) {
709           DFBColorAdjustment cb_adjust;
710
711           ret = dfbvideosink->layer->GetColorAdjustment (dfbvideosink->layer,
712               &cb_adjust);
713
714           if (ret != DFB_OK) {
715             GST_WARNING_OBJECT (dfbvideosink, "failed when getting color "
716                 "adjustment from layer");
717           }
718
719           if (cb_adjust.flags & DCAF_BRIGHTNESS) {
720             dfbvideosink->brightness = cb_adjust.brightness;
721           } else {
722             dfbvideosink->brightness = 0x8000;
723           }
724           if (cb_adjust.flags & DCAF_CONTRAST) {
725             dfbvideosink->contrast = cb_adjust.contrast;
726           } else {
727             dfbvideosink->contrast = 0x8000;
728           }
729           if (cb_adjust.flags & DCAF_HUE) {
730             dfbvideosink->hue = cb_adjust.hue;
731           } else {
732             dfbvideosink->hue = 0x8000;
733           }
734           if (cb_adjust.flags & DCAF_SATURATION) {
735             dfbvideosink->saturation = cb_adjust.saturation;
736           } else {
737             dfbvideosink->saturation = 0x8000;
738           }
739           GST_DEBUG_OBJECT (dfbvideosink, "brightness %d, contrast %d, "
740               "hue %d, saturation %d", dfbvideosink->brightness,
741               dfbvideosink->contrast, dfbvideosink->hue,
742               dfbvideosink->saturation);
743         }
744
745         g_list_free (channels_list);
746
747         gst_dfbvideosink_update_colorbalance (dfbvideosink);
748       }
749
750       dfbvideosink->layer->SetBackgroundColor (dfbvideosink->layer,
751           0x00, 0x00, 0x00, 0xFF);
752
753       dfbvideosink->layer->EnableCursor (dfbvideosink->layer, TRUE);
754
755       GST_DEBUG_OBJECT (dfbvideosink, "getting primary surface");
756       dfbvideosink->layer->GetSurface (dfbvideosink->layer,
757           &dfbvideosink->primary);
758
759       dfbvideosink->primary->SetBlittingFlags (dfbvideosink->primary,
760           DSBLIT_NOFX);
761     }
762
763     dfbvideosink->primary->GetPixelFormat (dfbvideosink->primary,
764         &dfbvideosink->pixel_format);
765   } else {
766     DFBSurfaceCapabilities s_caps;
767
768     GST_DEBUG_OBJECT (dfbvideosink, "getting pixel format from foreign "
769         "surface %p", dfbvideosink->ext_surface);
770     dfbvideosink->ext_surface->GetPixelFormat (dfbvideosink->ext_surface,
771         &dfbvideosink->pixel_format);
772     dfbvideosink->ext_surface->GetSize (dfbvideosink->ext_surface,
773         &dfbvideosink->out_width, &dfbvideosink->out_height);
774     dfbvideosink->ext_surface->GetCapabilities (dfbvideosink->ext_surface,
775         &s_caps);
776     if ((s_caps & DSCAPS_DOUBLE) || (s_caps & DSCAPS_TRIPLE)) {
777       dfbvideosink->backbuffer = TRUE;
778     } else {
779       dfbvideosink->backbuffer = FALSE;
780     }
781     GST_DEBUG_OBJECT (dfbvideosink, "external surface is %dx%d and uses %s "
782         "pixel format", dfbvideosink->out_width, dfbvideosink->out_height,
783         gst_dfbvideosink_get_format_name (dfbvideosink->pixel_format));
784   }
785
786   dfbvideosink->setup = TRUE;
787
788 beach:
789   return dfbvideosink->setup;
790 }
791
792 static void
793 gst_dfbvideosink_cleanup (GstDfbVideoSink * dfbvideosink)
794 {
795   g_return_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink));
796
797   GST_DEBUG_OBJECT (dfbvideosink, "cleaning up DirectFB environment");
798
799   /* Wait for our event thread */
800   if (dfbvideosink->event_thread) {
801     g_thread_join (dfbvideosink->event_thread);
802     dfbvideosink->event_thread = NULL;
803   }
804
805   if (dfbvideosink->event_buffer) {
806     dfbvideosink->event_buffer->Release (dfbvideosink->event_buffer);
807     dfbvideosink->event_buffer = NULL;
808   }
809
810   if (dfbvideosink->vmodes) {
811     GSList *walk = dfbvideosink->vmodes;
812
813     while (walk) {
814       g_free (walk->data);
815       walk = g_slist_next (walk);
816     }
817     g_slist_free (dfbvideosink->vmodes);
818     dfbvideosink->vmodes = NULL;
819   }
820
821   if (dfbvideosink->cb_channels) {
822     GList *walk = dfbvideosink->cb_channels;
823
824     while (walk) {
825       GstColorBalanceChannel *channel = walk->data;
826
827       g_object_unref (channel);
828       walk = g_list_next (walk);
829     }
830     g_list_free (dfbvideosink->cb_channels);
831     dfbvideosink->cb_channels = NULL;
832   }
833
834   if (dfbvideosink->buffer_pool) {
835     gst_dfbvideosink_bufferpool_clear (dfbvideosink);
836   }
837
838   if (dfbvideosink->primary) {
839     dfbvideosink->primary->Release (dfbvideosink->primary);
840     dfbvideosink->primary = NULL;
841   }
842
843   if (dfbvideosink->layer) {
844     dfbvideosink->layer->EnableCursor (dfbvideosink->layer, FALSE);
845     dfbvideosink->layer->Release (dfbvideosink->layer);
846     dfbvideosink->layer = NULL;
847   }
848
849   if (dfbvideosink->dfb) {
850     dfbvideosink->dfb->Release (dfbvideosink->dfb);
851     dfbvideosink->dfb = NULL;
852   }
853
854   dfbvideosink->setup = FALSE;
855 }
856
857 static DFBSurfacePixelFormat
858 gst_dfbvideosink_get_format_from_caps (GstCaps * caps)
859 {
860   GstStructure *structure;
861   gboolean ret;
862   DFBSurfacePixelFormat pixel_format = DSPF_UNKNOWN;
863
864   g_return_val_if_fail (GST_IS_CAPS (caps), DSPF_UNKNOWN);
865
866   structure = gst_caps_get_structure (caps, 0);
867
868   if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
869     gint bpp, depth;
870
871     ret = gst_structure_get_int (structure, "bpp", &bpp);
872     ret &= gst_structure_get_int (structure, "depth", &depth);
873
874     if (!ret) {
875       goto beach;
876     }
877
878     switch (bpp) {
879       case 16:
880         pixel_format = DSPF_RGB16;
881         break;
882       case 24:
883         pixel_format = DSPF_RGB24;
884         break;
885       case 32:
886         if (depth == 24) {
887           pixel_format = DSPF_RGB32;
888         } else if (depth == 32) {
889           pixel_format = DSPF_ARGB;
890         } else {
891           goto beach;
892         }
893         break;
894       default:
895         GST_WARNING ("unhandled RGB format, bpp %d, depth %d", bpp, depth);
896         goto beach;
897     }
898   } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
899     guint32 fourcc;
900
901     ret = gst_structure_get_fourcc (structure, "format", &fourcc);
902
903     if (!ret) {
904       GST_WARNING ("failed grabbing fourcc from caps %" GST_PTR_FORMAT, caps);
905       goto beach;
906     }
907
908     switch (fourcc) {
909       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
910         pixel_format = DSPF_I420;
911         break;
912       case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
913         pixel_format = DSPF_YV12;
914         break;
915       case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
916         pixel_format = DSPF_YUY2;
917         break;
918       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
919         pixel_format = DSPF_UYVY;
920         break;
921       default:
922         GST_WARNING ("unhandled YUV format %" GST_FOURCC_FORMAT,
923             GST_FOURCC_ARGS (fourcc));
924         goto beach;
925     }
926   } else {
927     GST_WARNING ("unknown caps name received %" GST_PTR_FORMAT, caps);
928     goto beach;
929   }
930
931 beach:
932   return pixel_format;
933 }
934
935 static GstCaps *
936 gst_dfbvideosink_get_caps_from_format (DFBSurfacePixelFormat format)
937 {
938   GstCaps *caps = NULL;
939   gboolean is_rgb = FALSE, is_yuv = FALSE;
940   gint bpp, depth;
941   guint32 fourcc;
942
943   g_return_val_if_fail (format != DSPF_UNKNOWN, NULL);
944
945   switch (format) {
946     case DSPF_RGB16:
947       is_rgb = TRUE;
948       bpp = 16;
949       depth = 16;
950       break;
951     case DSPF_RGB24:
952       is_rgb = TRUE;
953       bpp = 24;
954       depth = 24;
955       break;
956     case DSPF_RGB32:
957       is_rgb = TRUE;
958       bpp = 32;
959       depth = 24;
960       break;
961     case DSPF_ARGB:
962       is_rgb = TRUE;
963       bpp = 32;
964       depth = 32;
965       break;
966     case DSPF_YUY2:
967       is_yuv = TRUE;
968       fourcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
969       break;
970     case DSPF_UYVY:
971       is_yuv = TRUE;
972       fourcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
973       break;
974     case DSPF_I420:
975       is_yuv = TRUE;
976       fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
977       break;
978     case DSPF_YV12:
979       is_yuv = TRUE;
980       fourcc = GST_MAKE_FOURCC ('Y', 'V', '1', '2');
981       break;
982     default:
983       GST_WARNING ("unknown pixel format %s",
984           gst_dfbvideosink_get_format_name (format));
985       goto beach;
986   }
987
988   if (is_rgb) {
989     caps = gst_caps_new_simple ("video/x-raw-rgb",
990         "bpp", G_TYPE_INT, bpp, "depth", G_TYPE_INT, depth, NULL);
991   } else if (is_yuv) {
992     caps = gst_caps_new_simple ("video/x-raw-yuv",
993         "format", GST_TYPE_FOURCC, fourcc, NULL);
994   } else {
995     GST_WARNING ("neither rgb nor yuv, something strange here");
996   }
997
998 beach:
999   return caps;
1000 }
1001
1002 static gboolean
1003 gst_dfbvideosink_can_blit_from_format (GstDfbVideoSink * dfbvideosink,
1004     DFBSurfacePixelFormat format)
1005 {
1006   gboolean res = FALSE;
1007   DFBResult ret;
1008   IDirectFBSurface *surface = NULL;
1009   DFBSurfaceDescription s_dsc;
1010   DFBAccelerationMask mask;
1011   DFBDisplayLayerConfig dlc, prev_dlc;
1012
1013   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), FALSE);
1014
1015   /* Create a surface of desired format */
1016   s_dsc.flags = DSDESC_PIXELFORMAT | DSDESC_WIDTH | DSDESC_HEIGHT;
1017   s_dsc.pixelformat = format;
1018   s_dsc.width = 10;
1019   s_dsc.height = 10;
1020
1021   ret = dfbvideosink->dfb->CreateSurface (dfbvideosink->dfb, &s_dsc, &surface);
1022   if (ret != DFB_OK) {
1023     GST_WARNING_OBJECT (dfbvideosink, "failed creating surface with format %s",
1024         gst_dfbvideosink_get_format_name (format));
1025     goto beach;
1026   }
1027
1028   /* Backup layer configuration */
1029   ret = dfbvideosink->layer->GetConfiguration (dfbvideosink->layer, &prev_dlc);
1030   if (ret != DFB_OK) {
1031     GST_WARNING_OBJECT (dfbvideosink, "failed when getting current layer "
1032         "configuration");
1033     goto beach;
1034   }
1035
1036   /* Test configuration of the layer to this pixel format */
1037   dlc.flags = DLCONF_PIXELFORMAT;
1038   dlc.pixelformat = format;
1039
1040   ret = dfbvideosink->layer->TestConfiguration (dfbvideosink->layer, &dlc,
1041       NULL);
1042   if (ret != DFB_OK) {
1043     GST_DEBUG_OBJECT (dfbvideosink, "our layer refuses to operate in pixel "
1044         "format %s", gst_dfbvideosink_get_format_name (format));
1045     goto beach;
1046   }
1047
1048   ret = dfbvideosink->layer->SetConfiguration (dfbvideosink->layer, &dlc);
1049   if (ret != DFB_OK) {
1050     GST_WARNING_OBJECT (dfbvideosink, "our layer refuses to operate in pixel "
1051         "format, though this format was successfully tested earlied %s",
1052         gst_dfbvideosink_get_format_name (format));
1053     goto beach;
1054   }
1055
1056   ret = dfbvideosink->primary->GetAccelerationMask (dfbvideosink->primary,
1057       surface, &mask);
1058   if (ret != DFB_OK) {
1059     GST_WARNING_OBJECT (dfbvideosink, "failed getting acceleration mask");
1060     goto beach;
1061   }
1062
1063   /* Blitting from this format to our primary is accelerated */
1064   if (mask & DFXL_BLIT) {
1065     GST_DEBUG_OBJECT (dfbvideosink, "blitting from format %s to our primary "
1066         "is accelerated", gst_dfbvideosink_get_format_name (format));
1067     res = TRUE;
1068   } else {
1069     GST_DEBUG_OBJECT (dfbvideosink, "blitting from format %s to our primary "
1070         "is not accelerated", gst_dfbvideosink_get_format_name (format));
1071     res = FALSE;
1072   }
1073
1074   /* Restore original layer configuration */
1075   ret = dfbvideosink->layer->SetConfiguration (dfbvideosink->layer, &prev_dlc);
1076   if (ret != DFB_OK) {
1077     GST_WARNING_OBJECT (dfbvideosink, "failed when restoring layer "
1078         "configuration");
1079     goto beach;
1080   }
1081
1082 beach:
1083   if (surface) {
1084     surface->Release (surface);
1085   }
1086   return res;
1087 }
1088
1089 static gboolean
1090 gst_dfbvideosink_get_best_vmode (GstDfbVideoSink * dfbvideosink, gint v_width,
1091     gint v_height, GstDfbVMode * best_vmode)
1092 {
1093   GSList *walk = NULL;
1094   gboolean ret = FALSE;
1095   gint width, height, bpp;
1096   GstDfbVMode *vmode = NULL;
1097
1098   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), FALSE);
1099
1100   if (!dfbvideosink->vmodes) {
1101     goto beach;
1102   }
1103
1104   walk = dfbvideosink->vmodes;
1105
1106   vmode = (GstDfbVMode *) walk->data;
1107
1108   /* First mode */
1109   width = vmode->width;
1110   height = vmode->height;
1111   bpp = vmode->bpp;
1112
1113   while (walk) {
1114     gint wgap, hgap, best_wgap, best_hgap;
1115
1116     vmode = (GstDfbVMode *) walk->data;
1117
1118     /* What are the gaps */
1119     wgap = abs (vmode->width - v_width);
1120     hgap = abs (vmode->height - v_height);
1121     best_wgap = abs (width - v_width);
1122     best_hgap = abs (height - v_height);
1123
1124     /* If this mode is better we ll use that */
1125     if (wgap + hgap < best_wgap + best_hgap) {
1126       width = vmode->width;
1127       height = vmode->height;
1128       bpp = vmode->bpp;
1129     }
1130
1131     walk = g_slist_next (walk);
1132   }
1133
1134   GST_DEBUG_OBJECT (dfbvideosink, "found video mode %dx%d for input at %dx%d",
1135       width, height, v_width, v_height);
1136
1137   best_vmode->width = width;
1138   best_vmode->height = height;
1139   best_vmode->bpp = bpp;
1140
1141   ret = TRUE;
1142
1143 beach:
1144   return ret;
1145 }
1146
1147 static GstCaps *
1148 gst_dfbvideosink_getcaps (GstBaseSink * bsink)
1149 {
1150   GstDfbVideoSink *dfbvideosink;
1151   GstCaps *caps = NULL;
1152   gint i;
1153
1154   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1155
1156   if (!dfbvideosink->setup) {
1157     caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
1158             (dfbvideosink)));
1159     GST_DEBUG_OBJECT (dfbvideosink, "getcaps called and we are not setup yet, "
1160         "returning template %" GST_PTR_FORMAT, caps);
1161     goto beach;
1162   } else {
1163     GST_DEBUG_OBJECT (dfbvideosink, "getcaps called, checking our internal "
1164         "format");
1165     if (dfbvideosink->ext_surface) {
1166       /* We are not rendering to our own surface, returning this surface's
1167        *  pixel format */
1168       caps = gst_dfbvideosink_get_caps_from_format (dfbvideosink->pixel_format);
1169     } else {
1170       /* Try some formats */
1171       caps = gst_caps_new_empty ();
1172
1173       if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_RGB16)) {
1174         gst_caps_append (caps,
1175             gst_dfbvideosink_get_caps_from_format (DSPF_RGB16));
1176       }
1177       if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_RGB24)) {
1178         gst_caps_append (caps,
1179             gst_dfbvideosink_get_caps_from_format (DSPF_RGB24));
1180       }
1181       /* There's something wrong with RGB32, ffmpegcolorspace ?
1182          if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_RGB32)) {
1183          gst_caps_append (caps,
1184          gst_dfbvideosink_get_caps_from_format (DSPF_RGB32));
1185          } */
1186       if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_ARGB)) {
1187         gst_caps_append (caps,
1188             gst_dfbvideosink_get_caps_from_format (DSPF_ARGB));
1189       }
1190       if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_YUY2)) {
1191         gst_caps_append (caps,
1192             gst_dfbvideosink_get_caps_from_format (DSPF_YUY2));
1193       }
1194       if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_UYVY)) {
1195         gst_caps_append (caps,
1196             gst_dfbvideosink_get_caps_from_format (DSPF_UYVY));
1197       }
1198       if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_I420)) {
1199         gst_caps_append (caps,
1200             gst_dfbvideosink_get_caps_from_format (DSPF_I420));
1201       }
1202       if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_YV12)) {
1203         gst_caps_append (caps,
1204             gst_dfbvideosink_get_caps_from_format (DSPF_YV12));
1205       }
1206     }
1207   }
1208
1209   for (i = 0; i < gst_caps_get_size (caps); i++) {
1210     GstStructure *structure = gst_caps_get_structure (caps, i);
1211
1212     gst_structure_set (structure,
1213         "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1214         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1215         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1216
1217     if (!dfbvideosink->hw_scaling && dfbvideosink->par) {
1218       int nom, den;
1219
1220       nom = gst_value_get_fraction_numerator (dfbvideosink->par);
1221       den = gst_value_get_fraction_denominator (dfbvideosink->par);
1222       gst_structure_set (structure, "pixel-aspect-ratio",
1223           GST_TYPE_FRACTION, nom, den, NULL);
1224     }
1225   }
1226
1227   GST_DEBUG_OBJECT (dfbvideosink, "returning our caps %" GST_PTR_FORMAT, caps);
1228
1229 beach:
1230   return caps;
1231 }
1232
1233 static gboolean
1234 gst_dfbvideosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1235 {
1236   GstDfbVideoSink *dfbvideosink;
1237   GstStructure *structure;
1238   gboolean res, result = FALSE;
1239   gint video_width, video_height;
1240   const GValue *framerate;
1241   DFBSurfacePixelFormat pixel_format = DSPF_UNKNOWN;
1242
1243   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1244
1245   structure = gst_caps_get_structure (caps, 0);
1246   res = gst_structure_get_int (structure, "width", &video_width);
1247   res &= gst_structure_get_int (structure, "height", &video_height);
1248   framerate = gst_structure_get_value (structure, "framerate");
1249   res &= (framerate != NULL);
1250   if (!res) {
1251     goto beach;
1252   }
1253
1254   dfbvideosink->fps_n = gst_value_get_fraction_numerator (framerate);
1255   dfbvideosink->fps_d = gst_value_get_fraction_denominator (framerate);
1256
1257   pixel_format = gst_dfbvideosink_get_format_from_caps (caps);
1258
1259   GST_DEBUG_OBJECT (dfbvideosink, "setcaps called with %" GST_PTR_FORMAT, caps);
1260   GST_DEBUG_OBJECT (dfbvideosink, "our format is: %dx%d %s video at %d/%d fps",
1261       video_width, video_height,
1262       gst_dfbvideosink_get_format_name (pixel_format), dfbvideosink->fps_n,
1263       dfbvideosink->fps_d);
1264
1265   if (dfbvideosink->hw_scaling && dfbvideosink->par) {
1266     gint video_par_n, video_par_d;      /* video's PAR */
1267     gint display_par_n, display_par_d;  /* display's PAR */
1268     gint num, den;
1269     GValue display_ratio = { 0, };      /* display w/h ratio */
1270     const GValue *caps_par;
1271
1272     /* get aspect ratio from caps if it's present, and
1273      * convert video width and height to a display width and height
1274      * using wd / hd = wv / hv * PARv / PARd
1275      * the ratio wd / hd will be stored in display_ratio */
1276     g_value_init (&display_ratio, GST_TYPE_FRACTION);
1277
1278     /* get video's PAR */
1279     caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1280     if (caps_par) {
1281       video_par_n = gst_value_get_fraction_numerator (caps_par);
1282       video_par_d = gst_value_get_fraction_denominator (caps_par);
1283     } else {
1284       video_par_n = 1;
1285       video_par_d = 1;
1286     }
1287     /* get display's PAR */
1288     if (dfbvideosink->par) {
1289       display_par_n = gst_value_get_fraction_numerator (dfbvideosink->par);
1290       display_par_d = gst_value_get_fraction_denominator (dfbvideosink->par);
1291     } else {
1292       display_par_n = 1;
1293       display_par_d = 1;
1294     }
1295
1296     gst_value_set_fraction (&display_ratio,
1297         video_width * video_par_n * display_par_d,
1298         video_height * video_par_d * display_par_n);
1299
1300     num = gst_value_get_fraction_numerator (&display_ratio);
1301     den = gst_value_get_fraction_denominator (&display_ratio);
1302     GST_DEBUG_OBJECT (dfbvideosink,
1303         "video width/height: %dx%d, calculated display ratio: %d/%d",
1304         video_width, video_height, num, den);
1305
1306     /* now find a width x height that respects this display ratio.
1307      * prefer those that have one of w/h the same as the incoming video
1308      * using wd / hd = num / den */
1309
1310     /* start with same height, because of interlaced video */
1311     /* check hd / den is an integer scale factor, and scale wd with the PAR */
1312     if (video_height % den == 0) {
1313       GST_DEBUG_OBJECT (dfbvideosink, "keeping video height");
1314       GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_height * num / den;
1315       GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_height;
1316     } else if (video_width % num == 0) {
1317       GST_DEBUG_OBJECT (dfbvideosink, "keeping video width");
1318       GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_width;
1319       GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_width * den / num;
1320     } else {
1321       GST_DEBUG_OBJECT (dfbvideosink, "approximating while keeping height");
1322       GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_height * num / den;
1323       GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_height;
1324     }
1325     GST_DEBUG_OBJECT (dfbvideosink, "scaling to %dx%d",
1326         GST_VIDEO_SINK_WIDTH (dfbvideosink),
1327         GST_VIDEO_SINK_HEIGHT (dfbvideosink));
1328   } else {
1329     if (dfbvideosink->par) {
1330       const GValue *par;
1331
1332       par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1333       if (par) {
1334         if (gst_value_compare (par, dfbvideosink->par) != GST_VALUE_EQUAL) {
1335           goto wrong_aspect;
1336         }
1337       }
1338     }
1339     GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_width;
1340     GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_height;
1341   }
1342
1343   /* Try to adapt the video mode to the video geometry */
1344   if (dfbvideosink->dfb) {
1345     DFBResult ret;
1346     GstDfbVMode vmode;
1347
1348     GST_DEBUG_OBJECT (dfbvideosink, "trying to adapt the video mode to video "
1349         "geometry");
1350
1351     /* Set video mode and layer configuration appropriately */
1352     if (gst_dfbvideosink_get_best_vmode (dfbvideosink,
1353             GST_VIDEO_SINK_WIDTH (dfbvideosink),
1354             GST_VIDEO_SINK_HEIGHT (dfbvideosink), &vmode)) {
1355       DFBDisplayLayerConfig lc;
1356       gint width, height, bpp;
1357
1358       width = vmode.width;
1359       height = vmode.height;
1360       bpp = vmode.bpp;
1361
1362       GST_DEBUG_OBJECT (dfbvideosink, "setting video mode to %dx%d at %d bpp",
1363           width, height, bpp);
1364
1365       ret = dfbvideosink->dfb->SetVideoMode (dfbvideosink->dfb, width,
1366           height, bpp);
1367       if (ret != DFB_OK) {
1368         GST_WARNING_OBJECT (dfbvideosink, "failed setting video mode %dx%d "
1369             "at %d bpp", width, height, bpp);
1370       }
1371
1372       lc.flags = DLCONF_PIXELFORMAT;
1373       lc.pixelformat = pixel_format;
1374
1375       ret = dfbvideosink->layer->SetConfiguration (dfbvideosink->layer, &lc);
1376       if (ret != DFB_OK) {
1377         GST_WARNING_OBJECT (dfbvideosink, "failed setting layer pixelformat "
1378             "to %s", gst_dfbvideosink_get_format_name (pixel_format));
1379       } else {
1380         dfbvideosink->layer->GetConfiguration (dfbvideosink->layer, &lc);
1381         dfbvideosink->out_width = lc.width;
1382         dfbvideosink->out_height = lc.height;
1383         dfbvideosink->pixel_format = lc.pixelformat;
1384         GST_DEBUG_OBJECT (dfbvideosink, "layer %d now configured to %dx%d %s",
1385             dfbvideosink->layer_id, lc.width, lc.height,
1386             gst_dfbvideosink_get_format_name (lc.pixelformat));
1387       }
1388     }
1389   }
1390
1391   if (pixel_format != dfbvideosink->pixel_format) {
1392     GST_WARNING_OBJECT (dfbvideosink, "setcaps sent us a different pixel "
1393         "format %s", gst_dfbvideosink_get_format_name (pixel_format));
1394     goto beach;
1395   }
1396
1397   dfbvideosink->video_width = video_width;
1398   dfbvideosink->video_height = video_height;
1399
1400   result = TRUE;
1401
1402 beach:
1403   return result;
1404
1405 /* ERRORS */
1406 wrong_aspect:
1407   {
1408     GST_INFO_OBJECT (dfbvideosink, "pixel aspect ratio does not match");
1409     return FALSE;
1410   }
1411 }
1412
1413 static GstStateChangeReturn
1414 gst_dfbvideosink_change_state (GstElement * element, GstStateChange transition)
1415 {
1416   GstDfbVideoSink *dfbvideosink;
1417   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1418
1419   dfbvideosink = GST_DFBVIDEOSINK (element);
1420
1421   switch (transition) {
1422     case GST_STATE_CHANGE_NULL_TO_READY:
1423       dfbvideosink->running = TRUE;
1424       if (!dfbvideosink->setup) {
1425         if (!gst_dfbvideosink_setup (dfbvideosink)) {
1426           GST_DEBUG_OBJECT (dfbvideosink, "setup failed when changing state "
1427               "from NULL to READY");
1428           GST_ELEMENT_ERROR (dfbvideosink, RESOURCE, OPEN_WRITE,
1429               (NULL), ("Failed initializing DirectFB system"));
1430           return GST_STATE_CHANGE_FAILURE;
1431         }
1432       }
1433       break;
1434     case GST_STATE_CHANGE_READY_TO_PAUSED:
1435       /* Blank surface if we have one */
1436       if (dfbvideosink->ext_surface) {
1437         dfbvideosink->ext_surface->Clear (dfbvideosink->ext_surface,
1438             0x00, 0x00, 0x00, 0xFF);
1439       }
1440       if (dfbvideosink->primary) {
1441         dfbvideosink->primary->Clear (dfbvideosink->primary, 0x00, 0x00,
1442             0x00, 0xFF);
1443       }
1444       break;
1445     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1446       break;
1447     default:
1448       break;
1449   }
1450
1451   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1452   if (ret == GST_STATE_CHANGE_FAILURE)
1453     return ret;
1454
1455   switch (transition) {
1456     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1457       break;
1458     case GST_STATE_CHANGE_PAUSED_TO_READY:
1459       dfbvideosink->fps_d = 0;
1460       dfbvideosink->fps_n = 0;
1461       dfbvideosink->video_width = 0;
1462       dfbvideosink->video_height = 0;
1463
1464       if (dfbvideosink->buffer_pool) {
1465         gst_dfbvideosink_bufferpool_clear (dfbvideosink);
1466       }
1467       break;
1468     case GST_STATE_CHANGE_READY_TO_NULL:
1469       dfbvideosink->running = FALSE;
1470       if (dfbvideosink->setup) {
1471         gst_dfbvideosink_cleanup (dfbvideosink);
1472       }
1473       break;
1474     default:
1475       break;
1476   }
1477
1478   return ret;
1479 }
1480
1481 static void
1482 gst_dfbvideosink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1483     GstClockTime * start, GstClockTime * end)
1484 {
1485   GstDfbVideoSink *dfbvideosink;
1486
1487   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1488
1489   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1490     *start = GST_BUFFER_TIMESTAMP (buf);
1491     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1492       *end = *start + GST_BUFFER_DURATION (buf);
1493     } else {
1494       if (dfbvideosink->fps_n > 0) {
1495         *end =
1496             *start + (GST_SECOND * dfbvideosink->fps_d) / dfbvideosink->fps_n;
1497       }
1498     }
1499   }
1500 }
1501
1502 static GstFlowReturn
1503 gst_dfbvideosink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1504 {
1505   GstDfbVideoSink *dfbvideosink = NULL;
1506   DFBResult res;
1507   GstVideoRectangle dst, src, result;
1508   GstFlowReturn ret = GST_FLOW_OK;
1509   gboolean mem_cpy = TRUE;
1510
1511   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1512
1513   if (!dfbvideosink->setup) {
1514     ret = GST_FLOW_UNEXPECTED;
1515     goto beach;
1516   }
1517
1518   /* Is that a buffer we allocated ourselves ? */
1519   if (GST_IS_DFBSURFACE (buf)) {
1520     GstDfbSurface *tmp_surface = GST_DFBSURFACE (buf);
1521
1522     /* Does it have a surface ? */
1523     if (tmp_surface->surface) {
1524       mem_cpy = FALSE;
1525       GST_DEBUG_OBJECT (dfbvideosink, "we have a buffer (%p) we allocated "
1526           "ourselves and it has a surface, no memcpy then", buf);
1527     } else {
1528       /* No surface, that's a malloc */
1529       GST_DEBUG_OBJECT (dfbvideosink, "we have a buffer (%p) we allocated "
1530           "ourselves but it does not hold a surface", buf);
1531     }
1532   } else {
1533     /* Not our baby */
1534     GST_DEBUG_OBJECT (dfbvideosink, "we have a buffer (%p) we did not allocate",
1535         buf);
1536   }
1537
1538   if (mem_cpy) {
1539     IDirectFBSurface *dest = NULL, *surface = NULL;
1540     gpointer data;
1541     gint dest_pitch, src_pitch, line;
1542     GstStructure *structure;
1543
1544     /* As we are not blitting no acceleration is possible. If the surface is
1545      * too small we do clipping, if it's too big we center. Theoretically as 
1546      * we are using buffer_alloc, there's a chance that we have been able to 
1547      * do reverse caps negotiation */
1548
1549     if (dfbvideosink->ext_surface) {
1550       surface = dfbvideosink->ext_surface;
1551       GST_DEBUG_OBJECT (dfbvideosink, "memcpy to an external surface "
1552           "subsurface (vsync %d)", dfbvideosink->vsync);
1553     } else {
1554       surface = dfbvideosink->primary;
1555       GST_DEBUG_OBJECT (dfbvideosink, "memcpy to a primary subsurface "
1556           "(vsync %d)", dfbvideosink->vsync);
1557     }
1558
1559     /* Get the video frame geometry from the buffer caps */
1560     structure = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
1561     if (structure) {
1562       gst_structure_get_int (structure, "width", &src.w);
1563       gst_structure_get_int (structure, "height", &src.h);
1564     } else {
1565       src.w = dfbvideosink->video_width;
1566       src.h = dfbvideosink->video_height;
1567     }
1568     res = surface->GetSize (surface, &dst.w, &dst.h);
1569
1570     /* Center / Clip */
1571     gst_video_sink_center_rect (src, dst, &result, FALSE);
1572
1573     res = surface->GetSubSurface (surface, (DFBRectangle *) & result, &dest);
1574     if (res != DFB_OK) {
1575       GST_WARNING_OBJECT (dfbvideosink, "failed when getting a sub surface");
1576       ret = GST_FLOW_UNEXPECTED;
1577       goto beach;
1578     }
1579
1580     /* If we are not using Flip we wait for VSYNC before blit */
1581     if (!dfbvideosink->backbuffer && dfbvideosink->vsync) {
1582       dfbvideosink->layer->WaitForSync (dfbvideosink->layer);
1583     }
1584
1585     res = dest->Lock (dest, DSLF_WRITE, &data, &dest_pitch);
1586     if (res != DFB_OK) {
1587       GST_WARNING_OBJECT (dfbvideosink, "failed locking the external "
1588           "subsurface for writing");
1589       ret = GST_FLOW_ERROR;
1590       goto beach;
1591     }
1592
1593     /* Source video rowbytes */
1594     src_pitch = GST_BUFFER_SIZE (buf) / src.h;
1595
1596     /* Write each line respecting subsurface pitch */
1597     for (line = 0; line < result.h; line++) {
1598       /* We do clipping */
1599       memcpy (data, GST_BUFFER_DATA (buf) + (line * src_pitch),
1600           MIN (src_pitch, dest_pitch));
1601       data += dest_pitch;
1602     }
1603
1604     res = dest->Unlock (dest);
1605
1606     res = dest->Release (dest);
1607
1608     if (dfbvideosink->backbuffer) {
1609       if (dfbvideosink->vsync) {
1610         res = surface->Flip (surface, NULL, DSFLIP_ONSYNC);
1611       } else {
1612         res = surface->Flip (surface, NULL, DSFLIP_NONE);
1613       }
1614     }
1615   } else {
1616     /* Else we will [Stretch]Blit to our primary */
1617     GstDfbSurface *surface = GST_DFBSURFACE (buf);
1618
1619     GST_DEBUG_OBJECT (dfbvideosink, "blitting to a primary surface (vsync %d)",
1620         dfbvideosink->vsync);
1621
1622     src.w = GST_VIDEO_SINK_WIDTH (dfbvideosink);
1623     src.h = GST_VIDEO_SINK_HEIGHT (dfbvideosink);
1624
1625     dfbvideosink->primary->GetSize (dfbvideosink->primary, &dst.w, &dst.h);
1626
1627     /* Unlocking surface before blit */
1628     if (surface->locked) {
1629       surface->surface->Unlock (surface->surface);
1630       surface->locked = FALSE;
1631     }
1632
1633     gst_video_sink_center_rect (src, dst, &result, dfbvideosink->hw_scaling);
1634
1635     /* If we are not using Flip we wait for VSYNC before blit */
1636     if (!dfbvideosink->backbuffer && dfbvideosink->vsync) {
1637       dfbvideosink->layer->WaitForSync (dfbvideosink->layer);
1638     }
1639
1640     if (dfbvideosink->hw_scaling) {
1641       dfbvideosink->primary->StretchBlit (dfbvideosink->primary,
1642           surface->surface, NULL, (DFBRectangle *) & result);
1643     } else {
1644       DFBRectangle clip;
1645
1646       clip.x = clip.y = 0;
1647       clip.w = result.w;
1648       clip.h = result.h;
1649       dfbvideosink->primary->Blit (dfbvideosink->primary, surface->surface,
1650           &clip, result.x, result.y);
1651     }
1652
1653     if (dfbvideosink->backbuffer) {
1654       if (dfbvideosink->vsync) {
1655         dfbvideosink->primary->Flip (dfbvideosink->primary, NULL,
1656             DSFLIP_ONSYNC);
1657       } else {
1658         dfbvideosink->primary->Flip (dfbvideosink->primary, NULL, DSFLIP_NONE);
1659       }
1660     }
1661   }
1662
1663 beach:
1664   return ret;
1665 }
1666
1667 static void
1668 gst_dfbvideosink_bufferpool_clear (GstDfbVideoSink * dfbvideosink)
1669 {
1670   g_mutex_lock (dfbvideosink->pool_lock);
1671   while (dfbvideosink->buffer_pool) {
1672     GstDfbSurface *surface = dfbvideosink->buffer_pool->data;
1673
1674     dfbvideosink->buffer_pool = g_slist_delete_link (dfbvideosink->buffer_pool,
1675         dfbvideosink->buffer_pool);
1676     gst_dfbvideosink_surface_destroy (dfbvideosink, surface);
1677   }
1678   g_mutex_unlock (dfbvideosink->pool_lock);
1679 }
1680
1681 /* For every buffer request we create a custom buffer containing and
1682  * IDirectFBSurface or allocate a previously created one that's not used
1683  * anymore. */
1684 static GstFlowReturn
1685 gst_dfbvideosink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1686     GstCaps * caps, GstBuffer ** buf)
1687 {
1688   GstDfbVideoSink *dfbvideosink;
1689   GstDfbSurface *surface = NULL;
1690   GstFlowReturn ret = GST_FLOW_OK;
1691
1692   gboolean rev_nego = FALSE;
1693   gint width, height;
1694
1695   GstCaps *desired_caps = NULL;
1696   GstStructure *structure = NULL;
1697
1698   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1699
1700   GST_DEBUG_OBJECT (dfbvideosink, "a buffer of %d bytes was requested "
1701       "with caps %" GST_PTR_FORMAT " and offset %llu", size, caps, offset);
1702
1703   if (G_UNLIKELY (!dfbvideosink->setup)) {
1704     GST_DEBUG_OBJECT (dfbvideosink, "we are not setup yet, can't allocate!");
1705     *buf = NULL;
1706     return ret;
1707   }
1708
1709   desired_caps = gst_caps_copy (caps);
1710
1711   structure = gst_caps_get_structure (desired_caps, 0);
1712
1713   if (gst_structure_get_int (structure, "width", &width) &&
1714       gst_structure_get_int (structure, "height", &height)) {
1715     GstVideoRectangle dst, src, result;
1716     GstDfbVMode vmode;
1717
1718     /* If we can do hardware scaling we don't do reverse negotiation */
1719     if (dfbvideosink->hw_scaling) {
1720       goto alloc;
1721     }
1722
1723     /* Our desired geometry respects aspect ratio */
1724     src.w = width;
1725     src.h = height;
1726     /* We should adapt the destination to the most suitable video mode */
1727     if (gst_dfbvideosink_get_best_vmode (dfbvideosink, width, height, &vmode)) {
1728       dst.w = vmode.width;
1729       dst.h = vmode.height;
1730     } else {
1731       if (dfbvideosink->ext_surface) {
1732         dfbvideosink->ext_surface->GetSize (dfbvideosink->ext_surface, &dst.w,
1733             &dst.h);
1734       } else {
1735         dfbvideosink->primary->GetSize (dfbvideosink->primary, &dst.w, &dst.h);
1736       }
1737       dfbvideosink->out_width = dst.w;
1738       dfbvideosink->out_height = dst.h;
1739     }
1740
1741     gst_video_sink_center_rect (src, dst, &result, TRUE);
1742
1743     if (width != result.w || height != result.h) {
1744       GstPad *peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (dfbvideosink));
1745
1746       if (!GST_IS_PAD (peer)) {
1747         /* Is this situation possible ? */
1748         goto alloc;
1749       }
1750
1751       GST_DEBUG_OBJECT (dfbvideosink, "we would love to receive a %dx%d video",
1752           result.w, result.h);
1753       gst_structure_set (structure, "width", G_TYPE_INT, result.w, NULL);
1754       gst_structure_set (structure, "height", G_TYPE_INT, result.h, NULL);
1755
1756       /* PAR property overrides the X calculated one */
1757       if (dfbvideosink->par) {
1758         gint nom, den;
1759
1760         nom = gst_value_get_fraction_numerator (dfbvideosink->par);
1761         den = gst_value_get_fraction_denominator (dfbvideosink->par);
1762         gst_structure_set (structure, "pixel-aspect-ratio",
1763             GST_TYPE_FRACTION, nom, den, NULL);
1764       }
1765
1766       if (gst_pad_accept_caps (peer, desired_caps)) {
1767         gint bpp;
1768
1769         bpp = size / height / width;
1770         rev_nego = TRUE;
1771         width = result.w;
1772         height = result.h;
1773         size = bpp * width * height;
1774         GST_DEBUG_OBJECT (dfbvideosink, "peed pad accepts our desired caps %"
1775             GST_PTR_FORMAT " buffer size is now %d bytes", desired_caps, size);
1776       } else {
1777         GST_DEBUG_OBJECT (dfbvideosink, "peer pad does not accept our "
1778             "desired caps %" GST_PTR_FORMAT, desired_caps);
1779         rev_nego = FALSE;
1780         width = dfbvideosink->video_width;
1781         height = dfbvideosink->video_height;
1782       }
1783       gst_object_unref (peer);
1784     }
1785   }
1786
1787 alloc:
1788   /* Inspect our buffer pool */
1789   g_mutex_lock (dfbvideosink->pool_lock);
1790   while (dfbvideosink->buffer_pool) {
1791     surface = (GstDfbSurface *) dfbvideosink->buffer_pool->data;
1792
1793     if (surface) {
1794       /* Removing from the pool */
1795       dfbvideosink->buffer_pool =
1796           g_slist_delete_link (dfbvideosink->buffer_pool,
1797           dfbvideosink->buffer_pool);
1798
1799       /* If the surface is invalid for our need, destroy */
1800       if ((surface->width != width) ||
1801           (surface->height != height) ||
1802           (surface->pixel_format != dfbvideosink->pixel_format)) {
1803         gst_dfbvideosink_surface_destroy (dfbvideosink, surface);
1804         surface = NULL;
1805       } else {
1806         /* We found a suitable surface */
1807         break;
1808       }
1809     }
1810   }
1811   g_mutex_unlock (dfbvideosink->pool_lock);
1812
1813   /* We haven't found anything, creating a new one */
1814   if (!surface) {
1815     if (rev_nego) {
1816       surface = gst_dfbvideosink_surface_create (dfbvideosink, desired_caps,
1817           size);
1818     } else {
1819       surface = gst_dfbvideosink_surface_create (dfbvideosink, caps, size);
1820     }
1821   }
1822   /* Now we should have a surface, set appropriate caps on it */
1823   if (surface) {
1824     if (rev_nego) {
1825       gst_buffer_set_caps (GST_BUFFER (surface), desired_caps);
1826     } else {
1827       gst_buffer_set_caps (GST_BUFFER (surface), caps);
1828     }
1829   }
1830
1831   *buf = GST_BUFFER (surface);
1832
1833   gst_caps_unref (desired_caps);
1834
1835   return ret;
1836 }
1837
1838 /* Our subclass of GstBuffer */
1839
1840 static void
1841 gst_dfbsurface_finalize (GstDfbSurface * surface)
1842 {
1843   GstDfbVideoSink *dfbvideosink = NULL;
1844
1845   g_return_if_fail (surface != NULL);
1846
1847   dfbvideosink = surface->dfbvideosink;
1848   if (!dfbvideosink) {
1849     GST_WARNING_OBJECT (surface, "no sink found");
1850     goto beach;
1851   }
1852
1853   /* If our geometry changed we can't reuse that image. */
1854   if ((surface->width != dfbvideosink->video_width) ||
1855       (surface->height != dfbvideosink->video_height) ||
1856       (surface->pixel_format != dfbvideosink->pixel_format)) {
1857     GST_DEBUG_OBJECT (dfbvideosink, "destroy surface %p as its size changed "
1858         "%dx%d vs current %dx%d", surface, surface->width, surface->height,
1859         dfbvideosink->video_width, dfbvideosink->video_height);
1860     gst_dfbvideosink_surface_destroy (dfbvideosink, surface);
1861   } else {
1862     /* In that case we can reuse the image and add it to our image pool. */
1863     GST_DEBUG_OBJECT (dfbvideosink, "recycling surface %p in pool", surface);
1864     /* need to increment the refcount again to recycle */
1865     gst_buffer_ref (GST_BUFFER (surface));
1866     g_mutex_lock (dfbvideosink->pool_lock);
1867     dfbvideosink->buffer_pool = g_slist_prepend (dfbvideosink->buffer_pool,
1868         surface);
1869     g_mutex_unlock (dfbvideosink->pool_lock);
1870   }
1871
1872 beach:
1873   return;
1874 }
1875
1876 static void
1877 gst_dfbsurface_init (GstDfbSurface * surface, gpointer g_class)
1878 {
1879   surface->surface = NULL;
1880   surface->width = 0;
1881   surface->height = 0;
1882   surface->pixel_format = DSPF_UNKNOWN;
1883   surface->dfbvideosink = NULL;
1884 }
1885
1886 static void
1887 gst_dfbsurface_class_init (gpointer g_class, gpointer class_data)
1888 {
1889   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
1890
1891   surface_parent_class = g_type_class_peek_parent (g_class);
1892
1893   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
1894       gst_dfbsurface_finalize;
1895 }
1896
1897 GType
1898 gst_dfbsurface_get_type (void)
1899 {
1900   static GType _gst_dfbsurface_type;
1901
1902   if (G_UNLIKELY (_gst_dfbsurface_type == 0)) {
1903     static const GTypeInfo dfbsurface_info = {
1904       sizeof (GstBufferClass),
1905       NULL,
1906       NULL,
1907       gst_dfbsurface_class_init,
1908       NULL,
1909       NULL,
1910       sizeof (GstDfbSurface),
1911       0,
1912       (GInstanceInitFunc) gst_dfbsurface_init,
1913       NULL
1914     };
1915     _gst_dfbsurface_type = g_type_register_static (GST_TYPE_BUFFER,
1916         "GstDfbSurface", &dfbsurface_info, 0);
1917   }
1918   return _gst_dfbsurface_type;
1919 }
1920
1921 /* Interfaces stuff */
1922
1923 static gboolean
1924 gst_dfbvideosink_interface_supported (GstImplementsInterface * iface,
1925     GType type)
1926 {
1927   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_COLOR_BALANCE);
1928   return TRUE;
1929 }
1930
1931 static void
1932 gst_dfbvideosink_interface_init (GstImplementsInterfaceClass * klass)
1933 {
1934   klass->supported = gst_dfbvideosink_interface_supported;
1935 }
1936
1937 static void
1938 gst_dfbvideosink_navigation_send_event (GstNavigation * navigation,
1939     GstStructure * structure)
1940 {
1941   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (navigation);
1942   GstEvent *event;
1943   GstVideoRectangle src, dst, result;
1944   double x, y;
1945   GstPad *pad = NULL;
1946
1947   src.w = GST_VIDEO_SINK_WIDTH (dfbvideosink);
1948   src.h = GST_VIDEO_SINK_HEIGHT (dfbvideosink);
1949   dst.w = dfbvideosink->out_width;
1950   dst.h = dfbvideosink->out_height;
1951   gst_video_sink_center_rect (src, dst, &result, dfbvideosink->hw_scaling);
1952
1953   event = gst_event_new_navigation (structure);
1954
1955   /* Our coordinates can be wrong here if we centered the video */
1956
1957   /* Converting pointer coordinates to the non scaled geometry */
1958   if (gst_structure_get_double (structure, "pointer_x", &x)) {
1959     double old_x = x;
1960
1961     if (x >= result.x && x <= (result.x + result.w)) {
1962       x -= result.x;
1963       x *= dfbvideosink->video_width;
1964       x /= result.w;
1965     } else {
1966       x = 0;
1967     }
1968     GST_DEBUG_OBJECT (dfbvideosink, "translated navigation event x "
1969         "coordinate from %f to %f", old_x, x);
1970     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1971   }
1972   if (gst_structure_get_double (structure, "pointer_y", &y)) {
1973     double old_y = y;
1974
1975     if (y >= result.y && y <= (result.y + result.h)) {
1976       y -= result.y;
1977       y *= dfbvideosink->video_height;
1978       y /= result.h;
1979     } else {
1980       y = 0;
1981     }
1982     GST_DEBUG_OBJECT (dfbvideosink, "translated navigation event y "
1983         "coordinate from %fd to %fd", old_y, y);
1984     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1985   }
1986
1987   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (dfbvideosink));
1988
1989   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1990     gst_pad_send_event (pad, event);
1991
1992     gst_object_unref (pad);
1993   }
1994 }
1995
1996 static void
1997 gst_dfbvideosink_navigation_init (GstNavigationInterface * iface)
1998 {
1999   iface->send_event = gst_dfbvideosink_navigation_send_event;
2000 }
2001
2002 static void
2003 gst_dfbvideosink_update_colorbalance (GstDfbVideoSink * dfbvideosink)
2004 {
2005   g_return_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink));
2006
2007   if (dfbvideosink->layer) {
2008     DFBColorAdjustment cb_adjust;
2009
2010     cb_adjust.flags = DCAF_NONE;
2011
2012     if (dfbvideosink->brightness >= 0) {
2013       cb_adjust.flags |= DCAF_BRIGHTNESS;
2014     }
2015     if (dfbvideosink->contrast >= 0) {
2016       cb_adjust.flags |= DCAF_CONTRAST;
2017     }
2018     if (dfbvideosink->hue >= 0) {
2019       cb_adjust.flags |= DCAF_HUE;
2020     }
2021     if (dfbvideosink->saturation >= 0) {
2022       cb_adjust.flags |= DCAF_SATURATION;
2023     }
2024
2025     cb_adjust.brightness = dfbvideosink->brightness;
2026     cb_adjust.contrast = dfbvideosink->contrast;
2027     cb_adjust.hue = dfbvideosink->hue;
2028     cb_adjust.saturation = dfbvideosink->saturation;
2029
2030     GST_DEBUG_OBJECT (dfbvideosink, "updating colorbalance: flags %d "
2031         "brightness %d contrast %d hue %d saturation %d", cb_adjust.flags,
2032         cb_adjust.brightness, cb_adjust.contrast, cb_adjust.hue,
2033         cb_adjust.saturation);
2034     dfbvideosink->layer->SetColorAdjustment (dfbvideosink->layer, &cb_adjust);
2035   }
2036 }
2037
2038 static const GList *
2039 gst_dfbvideosink_colorbalance_list_channels (GstColorBalance * balance)
2040 {
2041   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (balance);
2042
2043   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), NULL);
2044
2045   return dfbvideosink->cb_channels;
2046 }
2047
2048 static void
2049 gst_dfbvideosink_colorbalance_set_value (GstColorBalance * balance,
2050     GstColorBalanceChannel * channel, gint value)
2051 {
2052   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (balance);
2053
2054   g_return_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink));
2055   g_return_if_fail (channel->label != NULL);
2056
2057   dfbvideosink->cb_changed = TRUE;
2058
2059   if (g_ascii_strcasecmp (channel->label, "HUE") == 0) {
2060     dfbvideosink->hue = value;
2061   } else if (g_ascii_strcasecmp (channel->label, "SATURATION") == 0) {
2062     dfbvideosink->saturation = value;
2063   } else if (g_ascii_strcasecmp (channel->label, "CONTRAST") == 0) {
2064     dfbvideosink->contrast = value;
2065   } else if (g_ascii_strcasecmp (channel->label, "BRIGHTNESS") == 0) {
2066     dfbvideosink->brightness = value;
2067   } else {
2068     GST_WARNING_OBJECT (dfbvideosink, "got an unknown channel %s",
2069         channel->label);
2070     return;
2071   }
2072
2073   gst_dfbvideosink_update_colorbalance (dfbvideosink);
2074 }
2075
2076 static gint
2077 gst_dfbvideosink_colorbalance_get_value (GstColorBalance * balance,
2078     GstColorBalanceChannel * channel)
2079 {
2080   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (balance);
2081   gint value = 0;
2082
2083   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), 0);
2084   g_return_val_if_fail (channel->label != NULL, 0);
2085
2086   if (g_ascii_strcasecmp (channel->label, "HUE") == 0) {
2087     value = dfbvideosink->hue;
2088   } else if (g_ascii_strcasecmp (channel->label, "SATURATION") == 0) {
2089     value = dfbvideosink->saturation;
2090   } else if (g_ascii_strcasecmp (channel->label, "CONTRAST") == 0) {
2091     value = dfbvideosink->contrast;
2092   } else if (g_ascii_strcasecmp (channel->label, "BRIGHTNESS") == 0) {
2093     value = dfbvideosink->brightness;
2094   } else {
2095     GST_WARNING_OBJECT (dfbvideosink, "got an unknown channel %s",
2096         channel->label);
2097   }
2098
2099   return value;
2100 }
2101
2102 static void
2103 gst_dfbvideosink_colorbalance_init (GstColorBalanceClass * iface)
2104 {
2105   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2106   iface->list_channels = gst_dfbvideosink_colorbalance_list_channels;
2107   iface->set_value = gst_dfbvideosink_colorbalance_set_value;
2108   iface->get_value = gst_dfbvideosink_colorbalance_get_value;
2109 }
2110
2111 /* Properties */
2112
2113 static void
2114 gst_dfbvideosink_set_property (GObject * object, guint prop_id,
2115     const GValue * value, GParamSpec * pspec)
2116 {
2117   GstDfbVideoSink *dfbvideosink;
2118
2119   g_return_if_fail (GST_IS_DFBVIDEOSINK (object));
2120   dfbvideosink = GST_DFBVIDEOSINK (object);
2121
2122   switch (prop_id) {
2123     case ARG_SURFACE:
2124       dfbvideosink->ext_surface = g_value_get_pointer (value);
2125       break;
2126     case ARG_HUE:
2127       dfbvideosink->hue = g_value_get_int (value);
2128       dfbvideosink->cb_changed = TRUE;
2129       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2130       break;
2131     case ARG_CONTRAST:
2132       dfbvideosink->contrast = g_value_get_int (value);
2133       dfbvideosink->cb_changed = TRUE;
2134       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2135       break;
2136     case ARG_BRIGHTNESS:
2137       dfbvideosink->brightness = g_value_get_int (value);
2138       dfbvideosink->cb_changed = TRUE;
2139       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2140       break;
2141     case ARG_SATURATION:
2142       dfbvideosink->saturation = g_value_get_int (value);
2143       dfbvideosink->cb_changed = TRUE;
2144       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2145       break;
2146     case ARG_PIXEL_ASPECT_RATIO:
2147       g_free (dfbvideosink->par);
2148       dfbvideosink->par = g_new0 (GValue, 1);
2149       g_value_init (dfbvideosink->par, GST_TYPE_FRACTION);
2150       if (!g_value_transform (value, dfbvideosink->par)) {
2151         GST_WARNING_OBJECT (dfbvideosink, "Could not transform string to "
2152             "aspect ratio");
2153         gst_value_set_fraction (dfbvideosink->par, 1, 1);
2154       }
2155       GST_DEBUG_OBJECT (dfbvideosink, "set PAR to %d/%d",
2156           gst_value_get_fraction_numerator (dfbvideosink->par),
2157           gst_value_get_fraction_denominator (dfbvideosink->par));
2158       break;
2159     case ARG_VSYNC:
2160       dfbvideosink->vsync = g_value_get_boolean (value);
2161       break;
2162     default:
2163       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2164       break;
2165   }
2166 }
2167
2168 static void
2169 gst_dfbvideosink_get_property (GObject * object, guint prop_id,
2170     GValue * value, GParamSpec * pspec)
2171 {
2172   GstDfbVideoSink *dfbvideosink;
2173
2174   g_return_if_fail (GST_IS_DFBVIDEOSINK (object));
2175   dfbvideosink = GST_DFBVIDEOSINK (object);
2176
2177   switch (prop_id) {
2178     case ARG_HUE:
2179       g_value_set_int (value, dfbvideosink->hue);
2180       break;
2181     case ARG_CONTRAST:
2182       g_value_set_int (value, dfbvideosink->contrast);
2183       break;
2184     case ARG_BRIGHTNESS:
2185       g_value_set_int (value, dfbvideosink->brightness);
2186       break;
2187     case ARG_SATURATION:
2188       g_value_set_int (value, dfbvideosink->saturation);
2189       break;
2190     case ARG_PIXEL_ASPECT_RATIO:
2191       if (dfbvideosink->par)
2192         g_value_transform (dfbvideosink->par, value);
2193       break;
2194     case ARG_VSYNC:
2195       g_value_set_boolean (value, dfbvideosink->vsync);
2196       break;
2197     default:
2198       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2199       break;
2200   }
2201 }
2202
2203 /* =========================================== */
2204 /*                                             */
2205 /*              Init & Class init              */
2206 /*                                             */
2207 /* =========================================== */
2208 static void
2209 gst_dfbvideosink_finalize (GObject * object)
2210 {
2211   GstDfbVideoSink *dfbvideosink;
2212
2213   dfbvideosink = GST_DFBVIDEOSINK (object);
2214
2215   if (dfbvideosink->par) {
2216     g_free (dfbvideosink->par);
2217     dfbvideosink->par = NULL;
2218   }
2219   if (dfbvideosink->pool_lock) {
2220     g_mutex_free (dfbvideosink->pool_lock);
2221     dfbvideosink->pool_lock = NULL;
2222   }
2223   if (dfbvideosink->setup) {
2224     gst_dfbvideosink_cleanup (dfbvideosink);
2225   }
2226
2227   G_OBJECT_CLASS (parent_class)->finalize (object);
2228 }
2229
2230 static void
2231 gst_dfbvideosink_init (GstDfbVideoSink * dfbvideosink)
2232 {
2233   dfbvideosink->pool_lock = g_mutex_new ();
2234   dfbvideosink->buffer_pool = NULL;
2235   dfbvideosink->video_height = dfbvideosink->out_width = 0;
2236   dfbvideosink->video_width = dfbvideosink->out_height = 0;
2237   dfbvideosink->fps_d = 0;
2238   dfbvideosink->fps_n = 0;
2239
2240   dfbvideosink->dfb = NULL;
2241   dfbvideosink->vmodes = NULL;
2242   dfbvideosink->layer_id = -1;
2243   dfbvideosink->layer = NULL;
2244   dfbvideosink->primary = NULL;
2245   dfbvideosink->event_buffer = NULL;
2246   dfbvideosink->event_thread = NULL;
2247
2248   dfbvideosink->ext_surface = NULL;
2249
2250   dfbvideosink->pixel_format = DSPF_UNKNOWN;
2251
2252   dfbvideosink->hw_scaling = FALSE;
2253   dfbvideosink->backbuffer = FALSE;
2254   dfbvideosink->vsync = TRUE;
2255   dfbvideosink->setup = FALSE;
2256   dfbvideosink->running = FALSE;
2257
2258   dfbvideosink->cb_channels = NULL;
2259   dfbvideosink->brightness = -1;
2260   dfbvideosink->contrast = -1;
2261   dfbvideosink->hue = -1;
2262   dfbvideosink->saturation = -1;
2263
2264   dfbvideosink->par = NULL;
2265 }
2266
2267 static void
2268 gst_dfbvideosink_base_init (gpointer g_class)
2269 {
2270   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2271
2272   gst_element_class_set_details (element_class, &gst_dfbvideosink_details);
2273
2274   gst_element_class_add_pad_template (element_class,
2275       gst_static_pad_template_get (&gst_dfbvideosink_sink_template_factory));
2276 }
2277
2278 static void
2279 gst_dfbvideosink_class_init (GstDfbVideoSinkClass * klass)
2280 {
2281   GObjectClass *gobject_class;
2282   GstElementClass *gstelement_class;
2283   GstBaseSinkClass *gstbasesink_class;
2284
2285   gobject_class = (GObjectClass *) klass;
2286   gstelement_class = (GstElementClass *) klass;
2287   gstbasesink_class = (GstBaseSinkClass *) klass;
2288
2289   parent_class = g_type_class_peek_parent (klass);
2290
2291   gobject_class->finalize = gst_dfbvideosink_finalize;
2292   gobject_class->set_property = gst_dfbvideosink_set_property;
2293   gobject_class->get_property = gst_dfbvideosink_get_property;
2294
2295   g_object_class_install_property (gobject_class, ARG_SURFACE,
2296       g_param_spec_pointer ("surface", "Surface",
2297           "The target surface for video", G_PARAM_WRITABLE));
2298   g_object_class_install_property (gobject_class, ARG_CONTRAST,
2299       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2300           0x0000, 0xFFFF, 0x8000, G_PARAM_READWRITE));
2301   g_object_class_install_property (gobject_class, ARG_BRIGHTNESS,
2302       g_param_spec_int ("brightness", "Brightness",
2303           "The brightness of the video", 0x0000, 0xFFFF, 0x8000,
2304           G_PARAM_READWRITE));
2305   g_object_class_install_property (gobject_class, ARG_HUE,
2306       g_param_spec_int ("hue", "Hue", "The hue of the video", 0x0000, 0xFFFF,
2307           0x8000, G_PARAM_READWRITE));
2308   g_object_class_install_property (gobject_class, ARG_SATURATION,
2309       g_param_spec_int ("saturation", "Saturation",
2310           "The saturation of the video", 0x0000, 0xFFFF, 0x8000,
2311           G_PARAM_READWRITE));
2312   g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO,
2313       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2314           "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2315   g_object_class_install_property (gobject_class, ARG_VSYNC,
2316       g_param_spec_boolean ("vsync", "Vertical synchronisation",
2317           "Wait for next vertical sync to draw frames", TRUE,
2318           G_PARAM_READWRITE));
2319
2320   gstelement_class->change_state = gst_dfbvideosink_change_state;
2321
2322   gstbasesink_class->get_caps = gst_dfbvideosink_getcaps;
2323   gstbasesink_class->set_caps = gst_dfbvideosink_setcaps;
2324   gstbasesink_class->buffer_alloc = gst_dfbvideosink_buffer_alloc;
2325   gstbasesink_class->get_times = gst_dfbvideosink_get_times;
2326   gstbasesink_class->preroll = gst_dfbvideosink_show_frame;
2327   gstbasesink_class->render = gst_dfbvideosink_show_frame;
2328 }
2329
2330 /* ============================================================= */
2331 /*                                                               */
2332 /*                       Public Methods                          */
2333 /*                                                               */
2334 /* ============================================================= */
2335
2336 /* =========================================== */
2337 /*                                             */
2338 /*          Object typing & Creation           */
2339 /*                                             */
2340 /* =========================================== */
2341
2342 GType
2343 gst_dfbvideosink_get_type (void)
2344 {
2345   static GType dfbvideosink_type = 0;
2346
2347   if (!dfbvideosink_type) {
2348     static const GTypeInfo dfbvideosink_info = {
2349       sizeof (GstDfbVideoSinkClass),
2350       gst_dfbvideosink_base_init,
2351       NULL,
2352       (GClassInitFunc) gst_dfbvideosink_class_init,
2353       NULL,
2354       NULL,
2355       sizeof (GstDfbVideoSink),
2356       0,
2357       (GInstanceInitFunc) gst_dfbvideosink_init,
2358     };
2359     static const GInterfaceInfo iface_info = {
2360       (GInterfaceInitFunc) gst_dfbvideosink_interface_init,
2361       NULL,
2362       NULL,
2363     };
2364     static const GInterfaceInfo navigation_info = {
2365       (GInterfaceInitFunc) gst_dfbvideosink_navigation_init,
2366       NULL,
2367       NULL,
2368     };
2369     static const GInterfaceInfo colorbalance_info = {
2370       (GInterfaceInitFunc) gst_dfbvideosink_colorbalance_init,
2371       NULL,
2372       NULL,
2373     };
2374
2375     dfbvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2376         "GstDfbVideoSink", &dfbvideosink_info, 0);
2377
2378     g_type_add_interface_static (dfbvideosink_type,
2379         GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
2380     g_type_add_interface_static (dfbvideosink_type, GST_TYPE_NAVIGATION,
2381         &navigation_info);
2382     g_type_add_interface_static (dfbvideosink_type, GST_TYPE_COLOR_BALANCE,
2383         &colorbalance_info);
2384   }
2385
2386   return dfbvideosink_type;
2387 }
2388
2389 static gboolean
2390 plugin_init (GstPlugin * plugin)
2391 {
2392   if (!gst_element_register (plugin, "dfbvideosink", GST_RANK_MARGINAL,
2393           GST_TYPE_DFBVIDEOSINK))
2394     return FALSE;
2395
2396   GST_DEBUG_CATEGORY_INIT (dfbvideosink_debug, "dfbvideosink", 0,
2397       "DirectFB video sink element");
2398
2399   return TRUE;
2400 }
2401
2402 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2403     GST_VERSION_MINOR,
2404     "dfbvideosink",
2405     "DirectFB video output plugin",
2406     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)