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