dfbvideosink: Fix 'implicit conversion from enumeration type 'DirectResult' to differ...
[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, gboolean accelerated)
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) && accelerated) {
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 if (!accelerated) {
1066     GST_DEBUG_OBJECT (dfbvideosink, "blitting from format %s to our primary "
1067         "is not accelerated", gst_dfbvideosink_get_format_name (format));
1068     res = TRUE;
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       gboolean accelerated = TRUE;
1169       caps = gst_caps_new_empty ();
1170
1171       do {
1172         if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_RGB16,
1173                 accelerated)) {
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                 accelerated)) {
1179           gst_caps_append (caps,
1180               gst_dfbvideosink_get_caps_from_format (DSPF_RGB24));
1181         }
1182         if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_RGB32,
1183                 accelerated)) {
1184           gst_caps_append (caps,
1185               gst_dfbvideosink_get_caps_from_format (DSPF_RGB32));
1186         }
1187         if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_ARGB,
1188                 accelerated)) {
1189           gst_caps_append (caps,
1190               gst_dfbvideosink_get_caps_from_format (DSPF_ARGB));
1191         }
1192         if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_YUY2,
1193                 accelerated)) {
1194           gst_caps_append (caps,
1195               gst_dfbvideosink_get_caps_from_format (DSPF_YUY2));
1196         }
1197         if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_UYVY,
1198                 accelerated)) {
1199           gst_caps_append (caps,
1200               gst_dfbvideosink_get_caps_from_format (DSPF_UYVY));
1201         }
1202         if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_I420,
1203                 accelerated)) {
1204           gst_caps_append (caps,
1205               gst_dfbvideosink_get_caps_from_format (DSPF_I420));
1206         }
1207         if (gst_dfbvideosink_can_blit_from_format (dfbvideosink, DSPF_YV12,
1208                 accelerated)) {
1209           gst_caps_append (caps,
1210               gst_dfbvideosink_get_caps_from_format (DSPF_YV12));
1211         }
1212         accelerated = !accelerated;
1213       } while (accelerated == FALSE);
1214     }
1215   }
1216
1217   for (i = 0; i < gst_caps_get_size (caps); i++) {
1218     GstStructure *structure = gst_caps_get_structure (caps, i);
1219
1220     gst_structure_set (structure,
1221         "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1222         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1223         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1224
1225     if (!dfbvideosink->hw_scaling && dfbvideosink->par) {
1226       int nom, den;
1227
1228       nom = gst_value_get_fraction_numerator (dfbvideosink->par);
1229       den = gst_value_get_fraction_denominator (dfbvideosink->par);
1230       gst_structure_set (structure, "pixel-aspect-ratio",
1231           GST_TYPE_FRACTION, nom, den, NULL);
1232     }
1233   }
1234
1235   GST_DEBUG_OBJECT (dfbvideosink, "returning our caps %" GST_PTR_FORMAT, caps);
1236
1237 beach:
1238   return caps;
1239 }
1240
1241 static gboolean
1242 gst_dfbvideosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1243 {
1244   GstDfbVideoSink *dfbvideosink;
1245   GstStructure *structure;
1246   gboolean res, result = FALSE;
1247   gint video_width, video_height;
1248   const GValue *framerate;
1249   DFBSurfacePixelFormat pixel_format = DSPF_UNKNOWN;
1250
1251   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1252
1253   structure = gst_caps_get_structure (caps, 0);
1254   res = gst_structure_get_int (structure, "width", &video_width);
1255   res &= gst_structure_get_int (structure, "height", &video_height);
1256   framerate = gst_structure_get_value (structure, "framerate");
1257   res &= (framerate != NULL);
1258   if (!res) {
1259     goto beach;
1260   }
1261
1262   dfbvideosink->fps_n = gst_value_get_fraction_numerator (framerate);
1263   dfbvideosink->fps_d = gst_value_get_fraction_denominator (framerate);
1264
1265   pixel_format = gst_dfbvideosink_get_format_from_caps (caps);
1266
1267   GST_DEBUG_OBJECT (dfbvideosink, "setcaps called with %" GST_PTR_FORMAT, caps);
1268   GST_DEBUG_OBJECT (dfbvideosink, "our format is: %dx%d %s video at %d/%d fps",
1269       video_width, video_height,
1270       gst_dfbvideosink_get_format_name (pixel_format), dfbvideosink->fps_n,
1271       dfbvideosink->fps_d);
1272
1273   if (dfbvideosink->hw_scaling && dfbvideosink->par) {
1274     gint video_par_n, video_par_d;      /* video's PAR */
1275     gint display_par_n, display_par_d;  /* display's PAR */
1276     gint num, den;
1277     GValue display_ratio = { 0, };      /* display w/h ratio */
1278     const GValue *caps_par;
1279
1280     /* get aspect ratio from caps if it's present, and
1281      * convert video width and height to a display width and height
1282      * using wd / hd = wv / hv * PARv / PARd
1283      * the ratio wd / hd will be stored in display_ratio */
1284     g_value_init (&display_ratio, GST_TYPE_FRACTION);
1285
1286     /* get video's PAR */
1287     caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1288     if (caps_par) {
1289       video_par_n = gst_value_get_fraction_numerator (caps_par);
1290       video_par_d = gst_value_get_fraction_denominator (caps_par);
1291     } else {
1292       video_par_n = 1;
1293       video_par_d = 1;
1294     }
1295     /* get display's PAR */
1296     if (dfbvideosink->par) {
1297       display_par_n = gst_value_get_fraction_numerator (dfbvideosink->par);
1298       display_par_d = gst_value_get_fraction_denominator (dfbvideosink->par);
1299     } else {
1300       display_par_n = 1;
1301       display_par_d = 1;
1302     }
1303
1304     gst_value_set_fraction (&display_ratio,
1305         video_width * video_par_n * display_par_d,
1306         video_height * video_par_d * display_par_n);
1307
1308     num = gst_value_get_fraction_numerator (&display_ratio);
1309     den = gst_value_get_fraction_denominator (&display_ratio);
1310     GST_DEBUG_OBJECT (dfbvideosink,
1311         "video width/height: %dx%d, calculated display ratio: %d/%d",
1312         video_width, video_height, num, den);
1313
1314     /* now find a width x height that respects this display ratio.
1315      * prefer those that have one of w/h the same as the incoming video
1316      * using wd / hd = num / den */
1317
1318     /* start with same height, because of interlaced video */
1319     /* check hd / den is an integer scale factor, and scale wd with the PAR */
1320     if (video_height % den == 0) {
1321       GST_DEBUG_OBJECT (dfbvideosink, "keeping video height");
1322       GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_height * num / den;
1323       GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_height;
1324     } else if (video_width % num == 0) {
1325       GST_DEBUG_OBJECT (dfbvideosink, "keeping video width");
1326       GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_width;
1327       GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_width * den / num;
1328     } else {
1329       GST_DEBUG_OBJECT (dfbvideosink, "approximating while keeping height");
1330       GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_height * num / den;
1331       GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_height;
1332     }
1333     GST_DEBUG_OBJECT (dfbvideosink, "scaling to %dx%d",
1334         GST_VIDEO_SINK_WIDTH (dfbvideosink),
1335         GST_VIDEO_SINK_HEIGHT (dfbvideosink));
1336   } else {
1337     if (dfbvideosink->par) {
1338       const GValue *par;
1339
1340       par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1341       if (par) {
1342         if (gst_value_compare (par, dfbvideosink->par) != GST_VALUE_EQUAL) {
1343           goto wrong_aspect;
1344         }
1345       }
1346     }
1347     GST_VIDEO_SINK_WIDTH (dfbvideosink) = video_width;
1348     GST_VIDEO_SINK_HEIGHT (dfbvideosink) = video_height;
1349   }
1350
1351   /* Try to adapt the video mode to the video geometry */
1352   if (dfbvideosink->dfb) {
1353     DFBResult ret;
1354     GstDfbVMode vmode;
1355
1356     GST_DEBUG_OBJECT (dfbvideosink, "trying to adapt the video mode to video "
1357         "geometry");
1358
1359     /* Set video mode and layer configuration appropriately */
1360     if (gst_dfbvideosink_get_best_vmode (dfbvideosink,
1361             GST_VIDEO_SINK_WIDTH (dfbvideosink),
1362             GST_VIDEO_SINK_HEIGHT (dfbvideosink), &vmode)) {
1363       DFBDisplayLayerConfig lc;
1364       gint width, height, bpp;
1365
1366       width = vmode.width;
1367       height = vmode.height;
1368       bpp = vmode.bpp;
1369
1370       GST_DEBUG_OBJECT (dfbvideosink, "setting video mode to %dx%d at %d bpp",
1371           width, height, bpp);
1372
1373       ret = dfbvideosink->dfb->SetVideoMode (dfbvideosink->dfb, width,
1374           height, bpp);
1375       if (ret != DFB_OK) {
1376         GST_WARNING_OBJECT (dfbvideosink, "failed setting video mode %dx%d "
1377             "at %d bpp", width, height, bpp);
1378       }
1379
1380       lc.flags = DLCONF_PIXELFORMAT;
1381       lc.pixelformat = pixel_format;
1382
1383       ret = dfbvideosink->layer->SetConfiguration (dfbvideosink->layer, &lc);
1384       if (ret != DFB_OK) {
1385         GST_WARNING_OBJECT (dfbvideosink, "failed setting layer pixelformat "
1386             "to %s", gst_dfbvideosink_get_format_name (pixel_format));
1387       } else {
1388         dfbvideosink->layer->GetConfiguration (dfbvideosink->layer, &lc);
1389         dfbvideosink->out_width = lc.width;
1390         dfbvideosink->out_height = lc.height;
1391         dfbvideosink->pixel_format = lc.pixelformat;
1392         GST_DEBUG_OBJECT (dfbvideosink, "layer %d now configured to %dx%d %s",
1393             dfbvideosink->layer_id, lc.width, lc.height,
1394             gst_dfbvideosink_get_format_name (lc.pixelformat));
1395       }
1396     }
1397   }
1398
1399   if (pixel_format != dfbvideosink->pixel_format) {
1400     GST_WARNING_OBJECT (dfbvideosink, "setcaps sent us a different pixel "
1401         "format %s", gst_dfbvideosink_get_format_name (pixel_format));
1402     goto beach;
1403   }
1404
1405   dfbvideosink->video_width = video_width;
1406   dfbvideosink->video_height = video_height;
1407
1408   result = TRUE;
1409
1410 beach:
1411   return result;
1412
1413 /* ERRORS */
1414 wrong_aspect:
1415   {
1416     GST_INFO_OBJECT (dfbvideosink, "pixel aspect ratio does not match");
1417     return FALSE;
1418   }
1419 }
1420
1421 static GstStateChangeReturn
1422 gst_dfbvideosink_change_state (GstElement * element, GstStateChange transition)
1423 {
1424   GstDfbVideoSink *dfbvideosink;
1425   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1426
1427   dfbvideosink = GST_DFBVIDEOSINK (element);
1428
1429   switch (transition) {
1430     case GST_STATE_CHANGE_NULL_TO_READY:
1431       dfbvideosink->running = TRUE;
1432       if (!dfbvideosink->setup) {
1433         if (!gst_dfbvideosink_setup (dfbvideosink)) {
1434           GST_DEBUG_OBJECT (dfbvideosink, "setup failed when changing state "
1435               "from NULL to READY");
1436           GST_ELEMENT_ERROR (dfbvideosink, RESOURCE, OPEN_WRITE,
1437               (NULL), ("Failed initializing DirectFB system"));
1438           return GST_STATE_CHANGE_FAILURE;
1439         }
1440       }
1441       break;
1442     case GST_STATE_CHANGE_READY_TO_PAUSED:
1443       /* Blank surface if we have one */
1444       if (dfbvideosink->ext_surface) {
1445         dfbvideosink->ext_surface->Clear (dfbvideosink->ext_surface,
1446             0x00, 0x00, 0x00, 0xFF);
1447       }
1448       if (dfbvideosink->primary) {
1449         dfbvideosink->primary->Clear (dfbvideosink->primary, 0x00, 0x00,
1450             0x00, 0xFF);
1451       }
1452       break;
1453     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1454       break;
1455     default:
1456       break;
1457   }
1458
1459   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1460   if (ret == GST_STATE_CHANGE_FAILURE)
1461     return ret;
1462
1463   switch (transition) {
1464     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1465       break;
1466     case GST_STATE_CHANGE_PAUSED_TO_READY:
1467       dfbvideosink->fps_d = 0;
1468       dfbvideosink->fps_n = 0;
1469       dfbvideosink->video_width = 0;
1470       dfbvideosink->video_height = 0;
1471
1472       if (dfbvideosink->buffer_pool) {
1473         gst_dfbvideosink_bufferpool_clear (dfbvideosink);
1474       }
1475       break;
1476     case GST_STATE_CHANGE_READY_TO_NULL:
1477       dfbvideosink->running = FALSE;
1478       if (dfbvideosink->setup) {
1479         gst_dfbvideosink_cleanup (dfbvideosink);
1480       }
1481       break;
1482     default:
1483       break;
1484   }
1485
1486   return ret;
1487 }
1488
1489 static void
1490 gst_dfbvideosink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1491     GstClockTime * start, GstClockTime * end)
1492 {
1493   GstDfbVideoSink *dfbvideosink;
1494
1495   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1496
1497   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1498     *start = GST_BUFFER_TIMESTAMP (buf);
1499     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1500       *end = *start + GST_BUFFER_DURATION (buf);
1501     } else {
1502       if (dfbvideosink->fps_n > 0) {
1503         *end =
1504             *start + (GST_SECOND * dfbvideosink->fps_d) / dfbvideosink->fps_n;
1505       }
1506     }
1507   }
1508 }
1509
1510 static GstFlowReturn
1511 gst_dfbvideosink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1512 {
1513   GstDfbVideoSink *dfbvideosink = NULL;
1514   DFBResult res;
1515   GstVideoRectangle dst, src, result;
1516   GstFlowReturn ret = GST_FLOW_OK;
1517   gboolean mem_cpy = TRUE;
1518
1519   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1520
1521   if (!dfbvideosink->setup) {
1522     ret = GST_FLOW_UNEXPECTED;
1523     goto beach;
1524   }
1525
1526   /* Is that a buffer we allocated ourselves ? */
1527   if (GST_IS_DFBSURFACE (buf)) {
1528     GstDfbSurface *tmp_surface = GST_DFBSURFACE (buf);
1529
1530     /* Does it have a surface ? */
1531     if (tmp_surface->surface) {
1532       mem_cpy = FALSE;
1533       GST_DEBUG_OBJECT (dfbvideosink, "we have a buffer (%p) we allocated "
1534           "ourselves and it has a surface, no memcpy then", buf);
1535     } else {
1536       /* No surface, that's a malloc */
1537       GST_DEBUG_OBJECT (dfbvideosink, "we have a buffer (%p) we allocated "
1538           "ourselves but it does not hold a surface", buf);
1539     }
1540   } else {
1541     /* Not our baby */
1542     GST_DEBUG_OBJECT (dfbvideosink, "we have a buffer (%p) we did not allocate",
1543         buf);
1544   }
1545
1546   if (mem_cpy) {
1547     IDirectFBSurface *dest = NULL, *surface = NULL;
1548     guint8 *data;
1549     gint dest_pitch, src_pitch, line;
1550     GstStructure *structure;
1551
1552     /* As we are not blitting no acceleration is possible. If the surface is
1553      * too small we do clipping, if it's too big we center. Theoretically as 
1554      * we are using buffer_alloc, there's a chance that we have been able to 
1555      * do reverse caps negotiation */
1556
1557     if (dfbvideosink->ext_surface) {
1558       surface = dfbvideosink->ext_surface;
1559       GST_DEBUG_OBJECT (dfbvideosink, "memcpy to an external surface "
1560           "subsurface (vsync %d)", dfbvideosink->vsync);
1561     } else {
1562       surface = dfbvideosink->primary;
1563       GST_DEBUG_OBJECT (dfbvideosink, "memcpy to a primary subsurface "
1564           "(vsync %d)", dfbvideosink->vsync);
1565     }
1566
1567     /* Get the video frame geometry from the buffer caps */
1568     structure = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
1569     if (structure) {
1570       gst_structure_get_int (structure, "width", &src.w);
1571       gst_structure_get_int (structure, "height", &src.h);
1572     } else {
1573       src.w = dfbvideosink->video_width;
1574       src.h = dfbvideosink->video_height;
1575     }
1576     res = surface->GetSize (surface, &dst.w, &dst.h);
1577
1578     /* Center / Clip */
1579     gst_video_sink_center_rect (src, dst, &result, FALSE);
1580
1581     res =
1582         surface->GetSubSurface (surface, (DFBRectangle *) (void *) &result,
1583         &dest);
1584     if (res != DFB_OK) {
1585       GST_WARNING_OBJECT (dfbvideosink, "failed when getting a sub surface");
1586       ret = GST_FLOW_UNEXPECTED;
1587       goto beach;
1588     }
1589
1590     /* If we are not using Flip we wait for VSYNC before blit */
1591     if (!dfbvideosink->backbuffer && dfbvideosink->vsync) {
1592       dfbvideosink->layer->WaitForSync (dfbvideosink->layer);
1593     }
1594
1595     res = dest->Lock (dest, DSLF_WRITE, (void *) &data, &dest_pitch);
1596     if (res != DFB_OK) {
1597       GST_WARNING_OBJECT (dfbvideosink, "failed locking the external "
1598           "subsurface for writing");
1599       ret = GST_FLOW_ERROR;
1600       goto beach;
1601     }
1602
1603     /* Source video rowbytes */
1604     src_pitch = GST_BUFFER_SIZE (buf) / src.h;
1605
1606     /* Write each line respecting subsurface pitch */
1607     for (line = 0; line < result.h; line++) {
1608       /* We do clipping */
1609       memcpy (data, GST_BUFFER_DATA (buf) + (line * src_pitch),
1610           MIN (src_pitch, dest_pitch));
1611       data += dest_pitch;
1612     }
1613
1614     res = dest->Unlock (dest);
1615
1616     dest->Release (dest);
1617
1618     if (dfbvideosink->backbuffer) {
1619       if (dfbvideosink->vsync) {
1620         res = surface->Flip (surface, NULL, DSFLIP_ONSYNC);
1621       } else {
1622         res = surface->Flip (surface, NULL, DSFLIP_NONE);
1623       }
1624     }
1625   } else {
1626     /* Else we will [Stretch]Blit to our primary */
1627     GstDfbSurface *surface = GST_DFBSURFACE (buf);
1628
1629     GST_DEBUG_OBJECT (dfbvideosink, "blitting to a primary surface (vsync %d)",
1630         dfbvideosink->vsync);
1631
1632     src.w = GST_VIDEO_SINK_WIDTH (dfbvideosink);
1633     src.h = GST_VIDEO_SINK_HEIGHT (dfbvideosink);
1634
1635     dfbvideosink->primary->GetSize (dfbvideosink->primary, &dst.w, &dst.h);
1636
1637     /* Unlocking surface before blit */
1638     if (surface->locked) {
1639       surface->surface->Unlock (surface->surface);
1640       surface->locked = FALSE;
1641     }
1642
1643     gst_video_sink_center_rect (src, dst, &result, dfbvideosink->hw_scaling);
1644
1645     /* If we are not using Flip we wait for VSYNC before blit */
1646     if (!dfbvideosink->backbuffer && dfbvideosink->vsync) {
1647       dfbvideosink->layer->WaitForSync (dfbvideosink->layer);
1648     }
1649
1650     if (dfbvideosink->hw_scaling) {
1651       dfbvideosink->primary->StretchBlit (dfbvideosink->primary,
1652           surface->surface, NULL, (DFBRectangle *) (void *) &result);
1653     } else {
1654       DFBRectangle clip;
1655
1656       clip.x = clip.y = 0;
1657       clip.w = result.w;
1658       clip.h = result.h;
1659       dfbvideosink->primary->Blit (dfbvideosink->primary, surface->surface,
1660           &clip, result.x, result.y);
1661     }
1662
1663     if (dfbvideosink->backbuffer) {
1664       if (dfbvideosink->vsync) {
1665         dfbvideosink->primary->Flip (dfbvideosink->primary, NULL,
1666             DSFLIP_ONSYNC);
1667       } else {
1668         dfbvideosink->primary->Flip (dfbvideosink->primary, NULL, DSFLIP_NONE);
1669       }
1670     }
1671   }
1672
1673 beach:
1674   return ret;
1675 }
1676
1677 static void
1678 gst_dfbvideosink_bufferpool_clear (GstDfbVideoSink * dfbvideosink)
1679 {
1680   g_mutex_lock (dfbvideosink->pool_lock);
1681   while (dfbvideosink->buffer_pool) {
1682     GstDfbSurface *surface = dfbvideosink->buffer_pool->data;
1683
1684     dfbvideosink->buffer_pool = g_slist_delete_link (dfbvideosink->buffer_pool,
1685         dfbvideosink->buffer_pool);
1686     gst_dfbvideosink_surface_destroy (dfbvideosink, surface);
1687   }
1688   g_mutex_unlock (dfbvideosink->pool_lock);
1689 }
1690
1691 /* For every buffer request we create a custom buffer containing and
1692  * IDirectFBSurface or allocate a previously created one that's not used
1693  * anymore. */
1694 static GstFlowReturn
1695 gst_dfbvideosink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1696     GstCaps * caps, GstBuffer ** buf)
1697 {
1698   GstDfbVideoSink *dfbvideosink;
1699   GstDfbSurface *surface = NULL;
1700   GstFlowReturn ret = GST_FLOW_OK;
1701
1702   gboolean rev_nego = FALSE;
1703   gint width, height;
1704
1705   GstCaps *desired_caps = NULL;
1706   GstStructure *structure = NULL;
1707
1708   dfbvideosink = GST_DFBVIDEOSINK (bsink);
1709
1710   GST_LOG_OBJECT (dfbvideosink, "a buffer of %u bytes was requested with caps "
1711       "%" GST_PTR_FORMAT " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1712
1713   if (G_UNLIKELY (!dfbvideosink->setup)) {
1714     GST_DEBUG_OBJECT (dfbvideosink, "we are not setup yet, can't allocate!");
1715     *buf = NULL;
1716     return ret;
1717   }
1718
1719   desired_caps = gst_caps_copy (caps);
1720
1721   structure = gst_caps_get_structure (desired_caps, 0);
1722
1723   if (gst_structure_get_int (structure, "width", &width) &&
1724       gst_structure_get_int (structure, "height", &height)) {
1725     GstVideoRectangle dst, src, result;
1726     GstDfbVMode vmode;
1727
1728     /* If we can do hardware scaling we don't do reverse negotiation */
1729     if (dfbvideosink->hw_scaling) {
1730       goto alloc;
1731     }
1732
1733     /* Our desired geometry respects aspect ratio */
1734     src.w = width;
1735     src.h = height;
1736     /* We should adapt the destination to the most suitable video mode */
1737     if (gst_dfbvideosink_get_best_vmode (dfbvideosink, width, height, &vmode)) {
1738       dst.w = vmode.width;
1739       dst.h = vmode.height;
1740     } else {
1741       if (dfbvideosink->ext_surface) {
1742         dfbvideosink->ext_surface->GetSize (dfbvideosink->ext_surface, &dst.w,
1743             &dst.h);
1744       } else {
1745         dfbvideosink->primary->GetSize (dfbvideosink->primary, &dst.w, &dst.h);
1746       }
1747       dfbvideosink->out_width = dst.w;
1748       dfbvideosink->out_height = dst.h;
1749     }
1750
1751     gst_video_sink_center_rect (src, dst, &result, TRUE);
1752
1753     if (width != result.w || height != result.h) {
1754       GstPad *peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (dfbvideosink));
1755
1756       if (!GST_IS_PAD (peer)) {
1757         /* Is this situation possible ? */
1758         goto alloc;
1759       }
1760
1761       GST_DEBUG_OBJECT (dfbvideosink, "we would love to receive a %dx%d video",
1762           result.w, result.h);
1763       gst_structure_set (structure, "width", G_TYPE_INT, result.w, NULL);
1764       gst_structure_set (structure, "height", G_TYPE_INT, result.h, NULL);
1765
1766       /* PAR property overrides the X calculated one */
1767       if (dfbvideosink->par) {
1768         gint nom, den;
1769
1770         nom = gst_value_get_fraction_numerator (dfbvideosink->par);
1771         den = gst_value_get_fraction_denominator (dfbvideosink->par);
1772         gst_structure_set (structure, "pixel-aspect-ratio",
1773             GST_TYPE_FRACTION, nom, den, NULL);
1774       }
1775
1776       if (gst_pad_accept_caps (peer, desired_caps)) {
1777         gint bpp;
1778
1779         bpp = size / height / width;
1780         rev_nego = TRUE;
1781         width = result.w;
1782         height = result.h;
1783         size = bpp * width * height;
1784         GST_DEBUG_OBJECT (dfbvideosink, "peed pad accepts our desired caps %"
1785             GST_PTR_FORMAT " buffer size is now %d bytes", desired_caps, size);
1786       } else {
1787         GST_DEBUG_OBJECT (dfbvideosink, "peer pad does not accept our "
1788             "desired caps %" GST_PTR_FORMAT, desired_caps);
1789         rev_nego = FALSE;
1790         width = dfbvideosink->video_width;
1791         height = dfbvideosink->video_height;
1792       }
1793       gst_object_unref (peer);
1794     }
1795   }
1796
1797 alloc:
1798   /* Inspect our buffer pool */
1799   g_mutex_lock (dfbvideosink->pool_lock);
1800   while (dfbvideosink->buffer_pool) {
1801     surface = (GstDfbSurface *) dfbvideosink->buffer_pool->data;
1802
1803     if (surface) {
1804       /* Removing from the pool */
1805       dfbvideosink->buffer_pool =
1806           g_slist_delete_link (dfbvideosink->buffer_pool,
1807           dfbvideosink->buffer_pool);
1808
1809       /* If the surface is invalid for our need, destroy */
1810       if ((surface->width != width) ||
1811           (surface->height != height) ||
1812           (surface->pixel_format != dfbvideosink->pixel_format)) {
1813         gst_dfbvideosink_surface_destroy (dfbvideosink, surface);
1814         surface = NULL;
1815       } else {
1816         /* We found a suitable surface */
1817         break;
1818       }
1819     }
1820   }
1821   g_mutex_unlock (dfbvideosink->pool_lock);
1822
1823   /* We haven't found anything, creating a new one */
1824   if (!surface) {
1825     if (rev_nego) {
1826       surface = gst_dfbvideosink_surface_create (dfbvideosink, desired_caps,
1827           size);
1828     } else {
1829       surface = gst_dfbvideosink_surface_create (dfbvideosink, caps, size);
1830     }
1831   }
1832   /* Now we should have a surface, set appropriate caps on it */
1833   if (surface) {
1834     if (rev_nego) {
1835       gst_buffer_set_caps (GST_BUFFER (surface), desired_caps);
1836     } else {
1837       gst_buffer_set_caps (GST_BUFFER (surface), caps);
1838     }
1839   }
1840
1841   *buf = GST_BUFFER (surface);
1842
1843   gst_caps_unref (desired_caps);
1844
1845   return ret;
1846 }
1847
1848 /* Our subclass of GstBuffer */
1849
1850 static void
1851 gst_dfbsurface_finalize (GstDfbSurface * surface)
1852 {
1853   GstDfbVideoSink *dfbvideosink = NULL;
1854
1855   g_return_if_fail (surface != NULL);
1856
1857   dfbvideosink = surface->dfbvideosink;
1858   if (!dfbvideosink) {
1859     GST_WARNING_OBJECT (surface, "no sink found");
1860     goto beach;
1861   }
1862
1863   /* If our geometry changed we can't reuse that image. */
1864   if ((surface->width != dfbvideosink->video_width) ||
1865       (surface->height != dfbvideosink->video_height) ||
1866       (surface->pixel_format != dfbvideosink->pixel_format)) {
1867     GST_DEBUG_OBJECT (dfbvideosink, "destroy surface %p as its size changed "
1868         "%dx%d vs current %dx%d", surface, surface->width, surface->height,
1869         dfbvideosink->video_width, dfbvideosink->video_height);
1870     gst_dfbvideosink_surface_destroy (dfbvideosink, surface);
1871   } else {
1872     /* In that case we can reuse the image and add it to our image pool. */
1873     GST_DEBUG_OBJECT (dfbvideosink, "recycling surface %p in pool", surface);
1874     /* need to increment the refcount again to recycle */
1875     gst_buffer_ref (GST_BUFFER (surface));
1876     g_mutex_lock (dfbvideosink->pool_lock);
1877     dfbvideosink->buffer_pool = g_slist_prepend (dfbvideosink->buffer_pool,
1878         surface);
1879     g_mutex_unlock (dfbvideosink->pool_lock);
1880   }
1881
1882 beach:
1883   return;
1884 }
1885
1886 static void
1887 gst_dfbsurface_init (GstDfbSurface * surface, gpointer g_class)
1888 {
1889   surface->surface = NULL;
1890   surface->width = 0;
1891   surface->height = 0;
1892   surface->pixel_format = DSPF_UNKNOWN;
1893   surface->dfbvideosink = NULL;
1894 }
1895
1896 static void
1897 gst_dfbsurface_class_init (gpointer g_class, gpointer class_data)
1898 {
1899   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
1900
1901   surface_parent_class = g_type_class_peek_parent (g_class);
1902
1903   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
1904       gst_dfbsurface_finalize;
1905 }
1906
1907 GType
1908 gst_dfbsurface_get_type (void)
1909 {
1910   static GType _gst_dfbsurface_type;
1911
1912   if (G_UNLIKELY (_gst_dfbsurface_type == 0)) {
1913     static const GTypeInfo dfbsurface_info = {
1914       sizeof (GstBufferClass),
1915       NULL,
1916       NULL,
1917       gst_dfbsurface_class_init,
1918       NULL,
1919       NULL,
1920       sizeof (GstDfbSurface),
1921       0,
1922       (GInstanceInitFunc) gst_dfbsurface_init,
1923       NULL
1924     };
1925     _gst_dfbsurface_type = g_type_register_static (GST_TYPE_BUFFER,
1926         "GstDfbSurface", &dfbsurface_info, 0);
1927   }
1928   return _gst_dfbsurface_type;
1929 }
1930
1931 /* Interfaces stuff */
1932
1933 static gboolean
1934 gst_dfbvideosink_interface_supported (GstImplementsInterface * iface,
1935     GType type)
1936 {
1937   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_COLOR_BALANCE);
1938   return TRUE;
1939 }
1940
1941 static void
1942 gst_dfbvideosink_interface_init (GstImplementsInterfaceClass * klass)
1943 {
1944   klass->supported = gst_dfbvideosink_interface_supported;
1945 }
1946
1947 static void
1948 gst_dfbvideosink_navigation_send_event (GstNavigation * navigation,
1949     GstStructure * structure)
1950 {
1951   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (navigation);
1952   GstEvent *event;
1953   GstVideoRectangle src, dst, result;
1954   double x, y, old_x, old_y;
1955   GstPad *pad = NULL;
1956
1957   src.w = GST_VIDEO_SINK_WIDTH (dfbvideosink);
1958   src.h = GST_VIDEO_SINK_HEIGHT (dfbvideosink);
1959   dst.w = dfbvideosink->out_width;
1960   dst.h = dfbvideosink->out_height;
1961   gst_video_sink_center_rect (src, dst, &result, dfbvideosink->hw_scaling);
1962
1963   event = gst_event_new_navigation (structure);
1964
1965   /* Our coordinates can be wrong here if we centered the video */
1966
1967   /* Converting pointer coordinates to the non scaled geometry */
1968   if (gst_structure_get_double (structure, "pointer_x", &old_x)) {
1969     x = old_x;
1970
1971     if (x >= result.x && x <= (result.x + result.w)) {
1972       x -= result.x;
1973       x *= dfbvideosink->video_width;
1974       x /= result.w;
1975     } else {
1976       x = 0;
1977     }
1978     GST_DEBUG_OBJECT (dfbvideosink, "translated navigation event x "
1979         "coordinate from %f to %f", old_x, x);
1980     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1981   }
1982   if (gst_structure_get_double (structure, "pointer_y", &old_y)) {
1983     y = old_y;
1984
1985     if (y >= result.y && y <= (result.y + result.h)) {
1986       y -= result.y;
1987       y *= dfbvideosink->video_height;
1988       y /= result.h;
1989     } else {
1990       y = 0;
1991     }
1992     GST_DEBUG_OBJECT (dfbvideosink, "translated navigation event y "
1993         "coordinate from %fd to %fd", old_y, y);
1994     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1995   }
1996
1997   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (dfbvideosink));
1998
1999   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
2000     gst_pad_send_event (pad, event);
2001
2002     gst_object_unref (pad);
2003   }
2004 }
2005
2006 static void
2007 gst_dfbvideosink_navigation_init (GstNavigationInterface * iface)
2008 {
2009   iface->send_event = gst_dfbvideosink_navigation_send_event;
2010 }
2011
2012 static void
2013 gst_dfbvideosink_update_colorbalance (GstDfbVideoSink * dfbvideosink)
2014 {
2015   g_return_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink));
2016
2017   if (dfbvideosink->layer) {
2018     DFBColorAdjustment cb_adjust;
2019
2020     cb_adjust.flags = DCAF_NONE;
2021
2022     if (dfbvideosink->brightness >= 0) {
2023       cb_adjust.flags |= DCAF_BRIGHTNESS;
2024     }
2025     if (dfbvideosink->contrast >= 0) {
2026       cb_adjust.flags |= DCAF_CONTRAST;
2027     }
2028     if (dfbvideosink->hue >= 0) {
2029       cb_adjust.flags |= DCAF_HUE;
2030     }
2031     if (dfbvideosink->saturation >= 0) {
2032       cb_adjust.flags |= DCAF_SATURATION;
2033     }
2034
2035     cb_adjust.brightness = dfbvideosink->brightness;
2036     cb_adjust.contrast = dfbvideosink->contrast;
2037     cb_adjust.hue = dfbvideosink->hue;
2038     cb_adjust.saturation = dfbvideosink->saturation;
2039
2040     GST_DEBUG_OBJECT (dfbvideosink, "updating colorbalance: flags %d "
2041         "brightness %d contrast %d hue %d saturation %d", cb_adjust.flags,
2042         cb_adjust.brightness, cb_adjust.contrast, cb_adjust.hue,
2043         cb_adjust.saturation);
2044     dfbvideosink->layer->SetColorAdjustment (dfbvideosink->layer, &cb_adjust);
2045   }
2046 }
2047
2048 static const GList *
2049 gst_dfbvideosink_colorbalance_list_channels (GstColorBalance * balance)
2050 {
2051   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (balance);
2052
2053   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), NULL);
2054
2055   return dfbvideosink->cb_channels;
2056 }
2057
2058 static void
2059 gst_dfbvideosink_colorbalance_set_value (GstColorBalance * balance,
2060     GstColorBalanceChannel * channel, gint value)
2061 {
2062   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (balance);
2063
2064   g_return_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink));
2065   g_return_if_fail (channel->label != NULL);
2066
2067   dfbvideosink->cb_changed = TRUE;
2068
2069   if (g_ascii_strcasecmp (channel->label, "HUE") == 0) {
2070     dfbvideosink->hue = value;
2071   } else if (g_ascii_strcasecmp (channel->label, "SATURATION") == 0) {
2072     dfbvideosink->saturation = value;
2073   } else if (g_ascii_strcasecmp (channel->label, "CONTRAST") == 0) {
2074     dfbvideosink->contrast = value;
2075   } else if (g_ascii_strcasecmp (channel->label, "BRIGHTNESS") == 0) {
2076     dfbvideosink->brightness = value;
2077   } else {
2078     GST_WARNING_OBJECT (dfbvideosink, "got an unknown channel %s",
2079         channel->label);
2080     return;
2081   }
2082
2083   gst_dfbvideosink_update_colorbalance (dfbvideosink);
2084 }
2085
2086 static gint
2087 gst_dfbvideosink_colorbalance_get_value (GstColorBalance * balance,
2088     GstColorBalanceChannel * channel)
2089 {
2090   GstDfbVideoSink *dfbvideosink = GST_DFBVIDEOSINK (balance);
2091   gint value = 0;
2092
2093   g_return_val_if_fail (GST_IS_DFBVIDEOSINK (dfbvideosink), 0);
2094   g_return_val_if_fail (channel->label != NULL, 0);
2095
2096   if (g_ascii_strcasecmp (channel->label, "HUE") == 0) {
2097     value = dfbvideosink->hue;
2098   } else if (g_ascii_strcasecmp (channel->label, "SATURATION") == 0) {
2099     value = dfbvideosink->saturation;
2100   } else if (g_ascii_strcasecmp (channel->label, "CONTRAST") == 0) {
2101     value = dfbvideosink->contrast;
2102   } else if (g_ascii_strcasecmp (channel->label, "BRIGHTNESS") == 0) {
2103     value = dfbvideosink->brightness;
2104   } else {
2105     GST_WARNING_OBJECT (dfbvideosink, "got an unknown channel %s",
2106         channel->label);
2107   }
2108
2109   return value;
2110 }
2111
2112 static void
2113 gst_dfbvideosink_colorbalance_init (GstColorBalanceClass * iface)
2114 {
2115   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2116   iface->list_channels = gst_dfbvideosink_colorbalance_list_channels;
2117   iface->set_value = gst_dfbvideosink_colorbalance_set_value;
2118   iface->get_value = gst_dfbvideosink_colorbalance_get_value;
2119 }
2120
2121 /* Properties */
2122
2123 static void
2124 gst_dfbvideosink_set_property (GObject * object, guint prop_id,
2125     const GValue * value, GParamSpec * pspec)
2126 {
2127   GstDfbVideoSink *dfbvideosink;
2128
2129   g_return_if_fail (GST_IS_DFBVIDEOSINK (object));
2130   dfbvideosink = GST_DFBVIDEOSINK (object);
2131
2132   switch (prop_id) {
2133     case ARG_SURFACE:
2134       dfbvideosink->ext_surface = g_value_get_pointer (value);
2135       break;
2136     case ARG_HUE:
2137       dfbvideosink->hue = g_value_get_int (value);
2138       dfbvideosink->cb_changed = TRUE;
2139       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2140       break;
2141     case ARG_CONTRAST:
2142       dfbvideosink->contrast = g_value_get_int (value);
2143       dfbvideosink->cb_changed = TRUE;
2144       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2145       break;
2146     case ARG_BRIGHTNESS:
2147       dfbvideosink->brightness = g_value_get_int (value);
2148       dfbvideosink->cb_changed = TRUE;
2149       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2150       break;
2151     case ARG_SATURATION:
2152       dfbvideosink->saturation = g_value_get_int (value);
2153       dfbvideosink->cb_changed = TRUE;
2154       gst_dfbvideosink_update_colorbalance (dfbvideosink);
2155       break;
2156     case ARG_PIXEL_ASPECT_RATIO:
2157       g_free (dfbvideosink->par);
2158       dfbvideosink->par = g_new0 (GValue, 1);
2159       g_value_init (dfbvideosink->par, GST_TYPE_FRACTION);
2160       if (!g_value_transform (value, dfbvideosink->par)) {
2161         GST_WARNING_OBJECT (dfbvideosink, "Could not transform string to "
2162             "aspect ratio");
2163         gst_value_set_fraction (dfbvideosink->par, 1, 1);
2164       }
2165       GST_DEBUG_OBJECT (dfbvideosink, "set PAR to %d/%d",
2166           gst_value_get_fraction_numerator (dfbvideosink->par),
2167           gst_value_get_fraction_denominator (dfbvideosink->par));
2168       break;
2169     case ARG_VSYNC:
2170       dfbvideosink->vsync = g_value_get_boolean (value);
2171       break;
2172     default:
2173       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2174       break;
2175   }
2176 }
2177
2178 static void
2179 gst_dfbvideosink_get_property (GObject * object, guint prop_id,
2180     GValue * value, GParamSpec * pspec)
2181 {
2182   GstDfbVideoSink *dfbvideosink;
2183
2184   g_return_if_fail (GST_IS_DFBVIDEOSINK (object));
2185   dfbvideosink = GST_DFBVIDEOSINK (object);
2186
2187   switch (prop_id) {
2188     case ARG_HUE:
2189       g_value_set_int (value, dfbvideosink->hue);
2190       break;
2191     case ARG_CONTRAST:
2192       g_value_set_int (value, dfbvideosink->contrast);
2193       break;
2194     case ARG_BRIGHTNESS:
2195       g_value_set_int (value, dfbvideosink->brightness);
2196       break;
2197     case ARG_SATURATION:
2198       g_value_set_int (value, dfbvideosink->saturation);
2199       break;
2200     case ARG_PIXEL_ASPECT_RATIO:
2201       if (dfbvideosink->par)
2202         g_value_transform (dfbvideosink->par, value);
2203       break;
2204     case ARG_VSYNC:
2205       g_value_set_boolean (value, dfbvideosink->vsync);
2206       break;
2207     default:
2208       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2209       break;
2210   }
2211 }
2212
2213 /* =========================================== */
2214 /*                                             */
2215 /*              Init & Class init              */
2216 /*                                             */
2217 /* =========================================== */
2218 static void
2219 gst_dfbvideosink_finalize (GObject * object)
2220 {
2221   GstDfbVideoSink *dfbvideosink;
2222
2223   dfbvideosink = GST_DFBVIDEOSINK (object);
2224
2225   if (dfbvideosink->par) {
2226     g_free (dfbvideosink->par);
2227     dfbvideosink->par = NULL;
2228   }
2229   if (dfbvideosink->pool_lock) {
2230     g_mutex_free (dfbvideosink->pool_lock);
2231     dfbvideosink->pool_lock = NULL;
2232   }
2233   if (dfbvideosink->setup) {
2234     gst_dfbvideosink_cleanup (dfbvideosink);
2235   }
2236
2237   G_OBJECT_CLASS (parent_class)->finalize (object);
2238 }
2239
2240 static void
2241 gst_dfbvideosink_init (GstDfbVideoSink * dfbvideosink)
2242 {
2243   dfbvideosink->pool_lock = g_mutex_new ();
2244   dfbvideosink->buffer_pool = NULL;
2245   dfbvideosink->video_height = dfbvideosink->out_height = 0;
2246   dfbvideosink->video_width = dfbvideosink->out_width = 0;
2247   dfbvideosink->fps_d = 0;
2248   dfbvideosink->fps_n = 0;
2249
2250   dfbvideosink->dfb = NULL;
2251   dfbvideosink->vmodes = NULL;
2252   dfbvideosink->layer_id = -1;
2253   dfbvideosink->layer = NULL;
2254   dfbvideosink->primary = NULL;
2255   dfbvideosink->event_buffer = NULL;
2256   dfbvideosink->event_thread = NULL;
2257
2258   dfbvideosink->ext_surface = NULL;
2259
2260   dfbvideosink->pixel_format = DSPF_UNKNOWN;
2261
2262   dfbvideosink->hw_scaling = FALSE;
2263   dfbvideosink->backbuffer = FALSE;
2264   dfbvideosink->vsync = TRUE;
2265   dfbvideosink->setup = FALSE;
2266   dfbvideosink->running = FALSE;
2267
2268   dfbvideosink->cb_channels = NULL;
2269   dfbvideosink->brightness = -1;
2270   dfbvideosink->contrast = -1;
2271   dfbvideosink->hue = -1;
2272   dfbvideosink->saturation = -1;
2273
2274   dfbvideosink->par = NULL;
2275 }
2276
2277 static void
2278 gst_dfbvideosink_base_init (gpointer g_class)
2279 {
2280   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2281
2282   gst_element_class_set_details_simple (element_class, "DirectFB video sink",
2283       "Sink/Video",
2284       "A DirectFB based videosink", "Julien Moutte <julien@moutte.net>");
2285
2286   gst_element_class_add_static_pad_template (element_class,
2287       &gst_dfbvideosink_sink_template_factory);
2288 }
2289
2290 static void
2291 gst_dfbvideosink_class_init (GstDfbVideoSinkClass * klass)
2292 {
2293   GObjectClass *gobject_class;
2294   GstElementClass *gstelement_class;
2295   GstBaseSinkClass *gstbasesink_class;
2296
2297   gobject_class = (GObjectClass *) klass;
2298   gstelement_class = (GstElementClass *) klass;
2299   gstbasesink_class = (GstBaseSinkClass *) klass;
2300
2301   parent_class = g_type_class_peek_parent (klass);
2302
2303   gobject_class->finalize = gst_dfbvideosink_finalize;
2304   gobject_class->set_property = gst_dfbvideosink_set_property;
2305   gobject_class->get_property = gst_dfbvideosink_get_property;
2306
2307   g_object_class_install_property (gobject_class, ARG_SURFACE,
2308       g_param_spec_pointer ("surface", "Surface",
2309           "The target surface for video",
2310           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
2311   g_object_class_install_property (gobject_class, ARG_CONTRAST,
2312       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2313           0x0000, 0xFFFF, 0x8000, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2314   g_object_class_install_property (gobject_class, ARG_BRIGHTNESS,
2315       g_param_spec_int ("brightness", "Brightness",
2316           "The brightness of the video", 0x0000, 0xFFFF, 0x8000,
2317           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2318   g_object_class_install_property (gobject_class, ARG_HUE,
2319       g_param_spec_int ("hue", "Hue", "The hue of the video", 0x0000, 0xFFFF,
2320           0x8000, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2321   g_object_class_install_property (gobject_class, ARG_SATURATION,
2322       g_param_spec_int ("saturation", "Saturation",
2323           "The saturation of the video", 0x0000, 0xFFFF, 0x8000,
2324           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2325   g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO,
2326       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2327           "The pixel aspect ratio of the device", "1/1",
2328           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2329   g_object_class_install_property (gobject_class, ARG_VSYNC,
2330       g_param_spec_boolean ("vsync", "Vertical synchronisation",
2331           "Wait for next vertical sync to draw frames", TRUE,
2332           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2333
2334   gstelement_class->change_state = gst_dfbvideosink_change_state;
2335
2336   gstbasesink_class->get_caps = gst_dfbvideosink_getcaps;
2337   gstbasesink_class->set_caps = gst_dfbvideosink_setcaps;
2338   gstbasesink_class->buffer_alloc = gst_dfbvideosink_buffer_alloc;
2339   gstbasesink_class->get_times = gst_dfbvideosink_get_times;
2340   gstbasesink_class->preroll = gst_dfbvideosink_show_frame;
2341   gstbasesink_class->render = gst_dfbvideosink_show_frame;
2342 }
2343
2344 /* ============================================================= */
2345 /*                                                               */
2346 /*                       Public Methods                          */
2347 /*                                                               */
2348 /* ============================================================= */
2349
2350 /* =========================================== */
2351 /*                                             */
2352 /*          Object typing & Creation           */
2353 /*                                             */
2354 /* =========================================== */
2355
2356 GType
2357 gst_dfbvideosink_get_type (void)
2358 {
2359   static GType dfbvideosink_type = 0;
2360
2361   if (!dfbvideosink_type) {
2362     static const GTypeInfo dfbvideosink_info = {
2363       sizeof (GstDfbVideoSinkClass),
2364       gst_dfbvideosink_base_init,
2365       NULL,
2366       (GClassInitFunc) gst_dfbvideosink_class_init,
2367       NULL,
2368       NULL,
2369       sizeof (GstDfbVideoSink),
2370       0,
2371       (GInstanceInitFunc) gst_dfbvideosink_init,
2372     };
2373     static const GInterfaceInfo iface_info = {
2374       (GInterfaceInitFunc) gst_dfbvideosink_interface_init,
2375       NULL,
2376       NULL,
2377     };
2378     static const GInterfaceInfo navigation_info = {
2379       (GInterfaceInitFunc) gst_dfbvideosink_navigation_init,
2380       NULL,
2381       NULL,
2382     };
2383     static const GInterfaceInfo colorbalance_info = {
2384       (GInterfaceInitFunc) gst_dfbvideosink_colorbalance_init,
2385       NULL,
2386       NULL,
2387     };
2388
2389     dfbvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2390         "GstDfbVideoSink", &dfbvideosink_info, 0);
2391
2392     g_type_add_interface_static (dfbvideosink_type,
2393         GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
2394     g_type_add_interface_static (dfbvideosink_type, GST_TYPE_NAVIGATION,
2395         &navigation_info);
2396     g_type_add_interface_static (dfbvideosink_type, GST_TYPE_COLOR_BALANCE,
2397         &colorbalance_info);
2398   }
2399
2400   return dfbvideosink_type;
2401 }
2402
2403 static gboolean
2404 plugin_init (GstPlugin * plugin)
2405 {
2406   if (!gst_element_register (plugin, "dfbvideosink", GST_RANK_MARGINAL,
2407           GST_TYPE_DFBVIDEOSINK))
2408     return FALSE;
2409
2410   GST_DEBUG_CATEGORY_INIT (dfbvideosink_debug, "dfbvideosink", 0,
2411       "DirectFB video sink element");
2412
2413   return TRUE;
2414 }
2415
2416 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2417     GST_VERSION_MINOR,
2418     "dfbvideosink",
2419     "DirectFB video output plugin",
2420     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)