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