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