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