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