kmssink: add me as author
[vjaquez-gstreamer:gst-plugins-bad.git] / sys / kms / gstkmssink.c
1 /* GStreamer
2  * Copyright (C) 2012 Texas Instruments
3  * Copyright (C) 2012 Collabora Ltd
4  *
5  * Authors:
6  *  Alessandro Decina <alessandro.decina@collabora.co.uk>
7  *  Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <string.h>             /* for strerror */
30 #include <unistd.h>             /* for close */
31
32 #include <gst/video/gstvideopool.h>
33 #include <gst/video/gstvideometa.h>
34 #include <gst/video/videocontext.h>
35
36 #include "gstkmssink.h"
37 #include "gstkmsmemory.h"
38
39 GST_DEBUG_CATEGORY_STATIC (gst_debug_kms_sink);
40 #define GST_CAT_DEFAULT gst_debug_kms_sink
41
42 GST_DEBUG_CATEGORY (GST_CAT_MEMORY);
43
44 G_DEFINE_TYPE (GstKMSSink, gst_kms_sink, GST_TYPE_VIDEO_SINK);
45
46 static void gst_kms_sink_reset (GstKMSSink * sink);
47
48 static GstStaticPadTemplate gst_kms_sink_template_factory =
49 GST_STATIC_PAD_TEMPLATE ("sink",
50     GST_PAD_SINK,
51     GST_PAD_ALWAYS,
52     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ UYVY, YUYV, NV12 }"))
53     );
54
55 enum
56 {
57   PROP_0,
58   PROP_PIXEL_ASPECT_RATIO,
59   PROP_FORCE_ASPECT_RATIO,
60   PROP_SCALE,
61   PROP_CONNECTOR,
62 };
63
64 static gboolean
65 gst_kms_sink_calculate_aspect_ratio (GstKMSSink * sink, gint width,
66     gint height, gint video_par_n, gint video_par_d)
67 {
68   guint calculated_par_n;
69   guint calculated_par_d;
70
71   if (!gst_video_calculate_display_ratio (&calculated_par_n, &calculated_par_d,
72           width, height, video_par_n, video_par_d, 1, 1)) {
73     GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
74         ("Error calculating the output display ratio of the video."));
75     return FALSE;
76   }
77   GST_DEBUG_OBJECT (sink,
78       "video width/height: %dx%d, calculated display ratio: %d/%d",
79       width, height, calculated_par_n, calculated_par_d);
80
81   /* now find a width x height that respects this display ratio.
82    * prefer those that have one of w/h the same as the incoming video
83    * using wd / hd = calculated_pad_n / calculated_par_d */
84
85   /* start with same height, because of interlaced video */
86   /* check hd / calculated_par_d is an integer scale factor, and scale
87    * wd with the PAR */
88   if (height % calculated_par_d == 0) {
89     GST_DEBUG_OBJECT (sink, "keeping video height");
90     GST_VIDEO_SINK_WIDTH (sink) = (guint)
91         gst_util_uint64_scale_int (height, calculated_par_n, calculated_par_d);
92     GST_VIDEO_SINK_HEIGHT (sink) = height;
93   } else if (width % calculated_par_n == 0) {
94     GST_DEBUG_OBJECT (sink, "keeping video width");
95     GST_VIDEO_SINK_WIDTH (sink) = width;
96     GST_VIDEO_SINK_HEIGHT (sink) = (guint)
97         gst_util_uint64_scale_int (width, calculated_par_d, calculated_par_n);
98   } else {
99     GST_DEBUG_OBJECT (sink, "approximating while keeping video height");
100     GST_VIDEO_SINK_WIDTH (sink) = (guint)
101         gst_util_uint64_scale_int (height, calculated_par_n, calculated_par_d);
102     GST_VIDEO_SINK_HEIGHT (sink) = height;
103   }
104   GST_DEBUG_OBJECT (sink, "scaling to %dx%d",
105       GST_VIDEO_SINK_WIDTH (sink), GST_VIDEO_SINK_HEIGHT (sink));
106
107   return TRUE;
108 }
109
110 static gboolean
111 gst_kms_sink_buffer_pool_is_ok (GstBufferPool * pool, GstCaps * newcaps,
112     gsize size)
113 {
114   GstCaps *oldcaps;
115   GstStructure *config;
116   guint bsize;
117   gboolean ret;
118
119   config = gst_buffer_pool_get_config (pool);
120   gst_buffer_pool_config_get_params (config, &oldcaps, &bsize, NULL, NULL);
121   ret = (size == bsize) && gst_caps_is_equal (newcaps, oldcaps);
122   gst_structure_free (config);
123
124   return ret;
125 }
126
127 static gboolean
128 gst_kms_sink_buffer_pool_setup (GstKMSSink * sink, GstCaps * caps)
129 {
130   GstBufferPool *newpool, *oldpool;
131   GstStructure *config;
132   gsize size;
133
134   oldpool = sink->pool;
135   size = GST_VIDEO_INFO_SIZE (&sink->info);
136
137   if (oldpool && gst_kms_sink_buffer_pool_is_ok (oldpool, caps, size))
138     return TRUE;
139
140   if (!sink->allocator)
141     sink->allocator = gst_kms_allocator_new (sink);
142
143   /* we need a new buffer pool */
144   newpool = gst_video_buffer_pool_new ();
145
146   config = gst_buffer_pool_get_config (newpool);
147   gst_buffer_pool_config_set_params (config, caps, size, 3, 0);
148   gst_buffer_pool_config_set_allocator (config, sink->allocator, NULL);
149
150   if (!gst_buffer_pool_set_config (newpool, config)) {
151     gst_object_unref (newpool);
152     return FALSE;
153   }
154
155   /* we don't activate the pool yet, this will be done by downstream after it
156    * has configured the pool. If downstream does not want our pool we will
157    * activate it when we render into it */
158   sink->pool = newpool;
159
160   /* unref the old sink */
161   if (oldpool) {
162     /* we don't deactivate, some elements might still be using it, it will
163      * be deactivated when the last ref is gone */
164     gst_object_unref (oldpool);
165   }
166
167   return TRUE;
168 }
169
170 static gboolean
171 gst_kms_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
172 {
173   GstKMSSink *sink;
174   gint width, height;
175
176   sink = GST_KMS_SINK (bsink);
177
178   if (!gst_video_info_from_caps (&sink->info, caps))
179     goto caps_parse_failed;
180
181   width = GST_VIDEO_INFO_WIDTH (&sink->info);
182   height = GST_VIDEO_INFO_HEIGHT (&sink->info);
183   if (width <= 0 || height <= 0)
184     goto invalid_size;
185
186   if (!gst_kms_sink_buffer_pool_setup (sink, caps))
187     goto buffer_pool_failed;
188
189   /* everything looks good so far */
190   sink->par_n = GST_VIDEO_INFO_PAR_N (&sink->info);
191   sink->par_d = GST_VIDEO_INFO_PAR_D (&sink->info);
192
193   if (sink->keep_aspect) {
194     gst_kms_sink_calculate_aspect_ratio (sink, width, height, sink->par_n,
195         sink->par_d);
196   } else {
197     GST_VIDEO_SINK_WIDTH (sink) = width;
198     GST_VIDEO_SINK_HEIGHT (sink) = height;
199   }
200
201   return TRUE;
202
203 fail:
204   return FALSE;
205
206 caps_parse_failed:
207   GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
208       ("Could not locate image format from caps %" GST_PTR_FORMAT, caps));
209   goto fail;
210
211 invalid_size:
212   GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL), ("Invalid image size."));
213   goto fail;
214
215 buffer_pool_failed:
216   GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
217       ("failed to set config to buffer pool."));
218   goto fail;
219 }
220
221 static void
222 gst_kms_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
223     GstClockTime * start, GstClockTime * end)
224 {
225   GstKMSSink *sink;
226
227   sink = GST_KMS_SINK (bsink);
228
229   if (GST_BUFFER_PTS_IS_VALID (buf)) {
230     *start = GST_BUFFER_PTS (buf);
231     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
232       *end = *start + GST_BUFFER_DURATION (buf);
233     } else {
234       if (GST_VIDEO_INFO_FPS_N (&sink->info) > 0) {
235         *end = *start +
236             gst_util_uint64_scale_int (GST_SECOND,
237             GST_VIDEO_INFO_FPS_D (&sink->info),
238             GST_VIDEO_INFO_FPS_N (&sink->info));
239       }
240     }
241   }
242 }
243
244 static gboolean
245 gst_kms_sink_find_mode_and_plane (GstKMSSink * sink, GstVideoRectangle * dim)
246 {
247   drmModeConnector *connector;
248   drmModeEncoder *encoder;
249   drmModeModeInfo *mode;
250   drmModePlane *plane;
251   int i, pipe;
252   gboolean ret;
253
254   ret = FALSE;
255   encoder = NULL;
256
257   /* First, find the connector & mode */
258   connector = drmModeGetConnector (sink->fd, sink->connector_id);
259   if (!connector)
260     goto error_no_connector;
261
262   if (connector->count_modes == 0)
263     goto error_no_mode;
264
265   /* Now get the encoder */
266   encoder = drmModeGetEncoder (sink->fd, connector->encoder_id);
267   if (!encoder)
268     goto error_no_encoder;
269
270   /* XXX: just pick the first available mode, which has the highest
271    * resolution. */
272   mode = &connector->modes[0];
273
274   dim->x = dim->y = 0;
275   dim->w = mode->hdisplay;
276   dim->h = mode->vdisplay;
277   GST_INFO_OBJECT (sink, "connector mode = %dx%d", dim->w, dim->h);
278
279   sink->crtc_id = encoder->crtc_id;
280
281   /* and figure out which crtc index it is: */
282   pipe = -1;
283   for (i = 0; i < sink->resources->count_crtcs; i++) {
284     if (sink->crtc_id == (int) sink->resources->crtcs[i]) {
285       pipe = i;
286       break;
287     }
288   }
289
290   if (pipe == -1)
291     goto error_no_crtc;
292
293   for (i = 0; i < sink->plane_resources->count_planes; i++) {
294     plane = drmModeGetPlane (sink->fd, sink->plane_resources->planes[i]);
295     if (plane->possible_crtcs & (1 << pipe)) {
296       sink->plane = plane;
297       break;
298     } else {
299       drmModeFreePlane (plane);
300     }
301   }
302
303   if (!sink->plane)
304     goto error_no_plane;
305
306   ret = TRUE;
307
308 fail:
309   if (encoder)
310     drmModeFreeEncoder (encoder);
311
312   if (connector)
313     drmModeFreeConnector (connector);
314
315   return ret;
316
317 error_no_connector:
318   GST_ERROR_OBJECT (sink, "could not get connector (%d): %s",
319       sink->connector_id, strerror (errno));
320   goto fail;
321
322 error_no_mode:
323   GST_ERROR_OBJECT (sink, "could not find a valid mode (count_modes %d)",
324       connector->count_modes);
325   goto fail;
326
327 error_no_encoder:
328   GST_ERROR_OBJECT (sink, "could not get encoder: %s", strerror (errno));
329   goto fail;
330
331 error_no_crtc:
332   GST_ERROR_OBJECT (sink, "couldn't find a crtc");
333   goto fail;
334
335 error_no_plane:
336   GST_ERROR_OBJECT (sink, "couldn't find a plane");
337   goto fail;
338 }
339
340
341 static GstFlowReturn
342 gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * inbuf)
343 {
344   GstKMSSink *sink;
345   GstKMSMemory *mem;
346   GstFlowReturn flow_ret;
347   GstVideoInfo *info;
348   guint32 fourcc;
349   GstVideoRectangle src, dest;
350   int ret;
351
352   ret = 0;
353   flow_ret = GST_FLOW_OK;
354   sink = GST_KMS_SINK (vsink);
355
356   src.x = src.y = 0;
357   src.w = GST_VIDEO_SINK_WIDTH (sink);
358   src.h = GST_VIDEO_SINK_HEIGHT (sink);
359
360   GST_TRACE_OBJECT (sink, "enter");
361
362   if (G_UNLIKELY (!sink->plane)) {
363     if (!gst_kms_sink_find_mode_and_plane (sink, &dest))
364       goto connector_not_found;
365     gst_video_sink_center_rect (src, dest, &sink->dst_rect, sink->scale);
366   }
367
368   /* TODO: add a meta for kms in order to validate the buffer */
369
370   info = &sink->info;
371   mem = gst_buffer_get_kms_memory (inbuf);
372   fourcc = gst_video_format_to_fourcc (GST_VIDEO_INFO_FORMAT (info));
373
374   if (G_UNLIKELY (mem->fb_id == -1)) {
375     /* if not mapped, do it! */
376     ret = drmModeAddFB2 (sink->fd, GST_VIDEO_SINK_WIDTH (sink),
377         GST_VIDEO_SINK_HEIGHT (sink), fourcc,
378         mem->handle, (uint32_t *) info->stride, (uint32_t *) info->offset,
379         &mem->fb_id, 0);
380     if (ret)
381       goto add_fb2_failed;
382   }
383
384   GST_DEBUG_OBJECT (sink, "crtc id = %d / plane id = %d / buffer id = %d",
385       sink->crtc_id, sink->plane->plane_id, mem->fb_id);
386
387   ret = drmModeSetPlane (sink->fd, sink->plane->plane_id,
388       sink->crtc_id, mem->fb_id, 0,
389       sink->dst_rect.x, sink->dst_rect.y, sink->dst_rect.w, sink->dst_rect.h,
390       /* source/cropping coordinates are given in Q16 */
391       src.x << 16, src.y << 16, src.w << 16, src.h << 16);
392
393   if (ret)
394     goto set_plane_failed;
395
396 out:
397   GST_TRACE_OBJECT (sink, "exit");
398   return flow_ret;
399
400 add_fb2_failed:
401   GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
402       (NULL), ("drmModeAddFB2 failed: %s (%d)", strerror (errno), errno));
403   flow_ret = GST_FLOW_ERROR;
404   goto out;
405
406 set_plane_failed:
407   GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
408       (NULL), ("drmModeSetPlane failed: %s (%d)", strerror (errno), errno));
409   flow_ret = GST_FLOW_ERROR;
410   goto out;
411
412 connector_not_found:
413   GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
414       (NULL), ("Could not post buffer on crtc %d: %s (%d)", sink->crtc_id,
415           strerror (errno), ret));
416   flow_ret = GST_FLOW_ERROR;
417   goto out;
418 }
419
420 static void
421 gst_kms_sink_set_property (GObject * object, guint prop_id,
422     const GValue * value, GParamSpec * pspec)
423 {
424   GstKMSSink *sink;
425
426   g_return_if_fail (GST_IS_KMS_SINK (object));
427
428   sink = GST_KMS_SINK (object);
429
430   switch (prop_id) {
431     case PROP_FORCE_ASPECT_RATIO:
432       sink->keep_aspect = g_value_get_boolean (value);
433       break;
434     case PROP_SCALE:
435       sink->scale = g_value_get_boolean (value);
436       break;
437     case PROP_CONNECTOR:
438       sink->connector_id = g_value_get_int (value);
439       break;
440     case PROP_PIXEL_ASPECT_RATIO:
441     {
442       GValue *tmp;
443
444       tmp = g_new0 (GValue, 1);
445       g_value_init (tmp, GST_TYPE_FRACTION);
446
447       if (!g_value_transform (value, tmp)) {
448         GST_WARNING_OBJECT (sink, "Could not transform string to aspect ratio");
449         g_free (tmp);
450       } else {
451         sink->par_n = gst_value_get_fraction_numerator (tmp);
452         sink->par_d = gst_value_get_fraction_denominator (tmp);
453         GST_DEBUG_OBJECT (sink, "set PAR to %d/%d", sink->par_n, sink->par_d);
454       }
455     }
456       break;
457     default:
458       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
459       break;
460   }
461 }
462
463 static void
464 gst_kms_sink_get_property (GObject * object, guint prop_id,
465     GValue * value, GParamSpec * pspec)
466 {
467   GstKMSSink *sink;
468
469   g_return_if_fail (GST_IS_KMS_SINK (object));
470
471   sink = GST_KMS_SINK (object);
472
473   switch (prop_id) {
474     case PROP_FORCE_ASPECT_RATIO:
475       g_value_set_boolean (value, sink->keep_aspect);
476       break;
477     case PROP_SCALE:
478       g_value_set_boolean (value, sink->scale);
479       break;
480     case PROP_CONNECTOR:
481       g_value_set_int (value, sink->connector_id);
482       break;
483     case PROP_PIXEL_ASPECT_RATIO:
484     {
485       char *v = g_strdup_printf ("%d/%d", sink->par_n, sink->par_d);
486       g_value_take_string (value, v);
487       break;
488     }
489     default:
490       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
491       break;
492   }
493 }
494
495 static void
496 gst_kms_sink_reset (GstKMSSink * sink)
497 {
498   if (sink->pool) {
499     gst_object_unref (sink->pool);
500     sink->pool = NULL;
501   }
502
503   if (sink->allocator) {
504     gst_object_unref (sink->allocator);
505     sink->allocator = NULL;
506   }
507
508   if (sink->plane) {
509     drmModeFreePlane (sink->plane);
510     sink->plane = NULL;
511   }
512
513   if (sink->plane_resources) {
514     drmModeFreePlaneResources (sink->plane_resources);
515     sink->plane_resources = NULL;
516   }
517
518   if (sink->resources) {
519     drmModeFreeResources (sink->resources);
520     sink->resources = NULL;
521   }
522
523
524   if (sink->fd != -1) {
525     close (sink->fd);
526     sink->fd = -1;
527   }
528
529   sink->par_n = sink->par_d = 1;
530
531   memset (&sink->dst_rect, 0, sizeof (GstVideoRectangle));
532   memset (&sink->info, 0, sizeof (GstVideoInfo));
533
534   sink->connector_id = -1;
535 }
536
537 static gboolean
538 gst_kms_sink_start (GstBaseSink * bsink)
539 {
540   GstKMSSink *sink;
541
542   sink = GST_KMS_SINK (bsink);
543
544   sink->fd = drmOpen ("omapdrm", NULL);
545   if (sink->fd < 0)
546     goto open_failed;
547
548   sink->resources = drmModeGetResources (sink->fd);
549   if (sink->resources == NULL)
550     goto resources_failed;
551
552   sink->plane_resources = drmModeGetPlaneResources (sink->fd);
553   if (sink->plane_resources == NULL)
554     goto plane_resources_failed;
555
556   return TRUE;
557
558 fail:
559   gst_kms_sink_reset (sink);
560   return FALSE;
561
562 open_failed:
563   GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
564       (NULL), ("drmOpen failed: %s (%d)", strerror (errno), errno));
565   goto fail;
566
567 resources_failed:
568   GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
569       (NULL), ("drmModeGetResources failed: %s (%d)", strerror (errno), errno));
570   goto fail;
571
572 plane_resources_failed:
573   GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
574       (NULL), ("drmModeGetPlaneResources failed: %s (%d)",
575           strerror (errno), errno));
576   goto fail;
577 }
578
579 static gboolean
580 gst_kms_sink_stop (GstBaseSink * bsink)
581 {
582   GstKMSSink *sink;
583
584   sink = GST_KMS_SINK (bsink);
585   gst_kms_sink_reset (sink);
586
587   return TRUE;
588 }
589
590 static gboolean
591 gst_kms_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
592 {
593   GstKMSSink *sink;
594   GstCaps *caps;
595   GstVideoInfo info;
596   guint size;
597
598   sink = GST_KMS_SINK (bsink);
599
600   GST_TRACE_OBJECT (sink, "allocation proposed");
601
602   /* need_pool is NULL because we always setup a pool */
603   gst_query_parse_allocation (query, &caps, NULL);
604
605   if (!caps)
606     goto no_caps;
607
608   if (!gst_video_info_from_caps (&sink->info, caps))
609     goto invalid_caps;
610
611   size = GST_VIDEO_INFO_SIZE (&info);
612   if (!gst_kms_sink_buffer_pool_setup (sink, caps))
613     goto config_failed;
614
615   /* we need at least 3 buffers because rob said so */
616   gst_query_add_allocation_pool (query, sink->pool, size, 3, 0);
617
618   /* we need to set our allocator in the query */
619   gst_query_add_allocation_param (query, sink->allocator, NULL);
620
621   /* we also support video metadata */
622   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
623   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
624
625   return TRUE;
626
627   /* ERRORS */
628 no_caps:
629   {
630     GST_DEBUG_OBJECT (bsink, "no caps specified");
631     return FALSE;
632   }
633 invalid_caps:
634   {
635     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
636     return FALSE;
637   }
638 config_failed:
639   {
640     GST_DEBUG_OBJECT (bsink, "failed setting config");
641     return FALSE;
642   }
643 }
644
645 static gboolean
646 gst_kms_sink_query (GstBaseSink * bsink, GstQuery * query)
647 {
648   GstKMSSink *sink;
649   const char **types;
650   int i;
651
652   sink = GST_KMS_SINK (bsink);
653
654   GST_DEBUG_OBJECT (sink, "Got query: %s", GST_QUERY_TYPE_NAME (query));
655
656   if (sink->fd < 0)
657     goto bail;
658
659   types = gst_video_context_query_get_supported_types (query);
660   if (!types)
661     goto bail;
662
663   for (i = 0; types[i]; i++) {
664     if (!strcmp (types[i], "drm-fd")) {
665       GstStructure *structure;
666
667       structure = gst_query_writable_structure (query);
668       gst_structure_set (structure, "video-context-type", G_TYPE_STRING,
669           "drm-fb", "video-context", G_TYPE_INT, sink->fd, NULL);
670     }
671   }
672
673 bail:
674   return GST_BASE_SINK_CLASS (gst_kms_sink_parent_class)->query (bsink, query);
675 }
676
677 static void
678 gst_kms_sink_finalize (GObject * object)
679 {
680   GstKMSSink *sink;
681
682   sink = GST_KMS_SINK (object);
683   gst_kms_sink_reset (sink);
684
685   G_OBJECT_CLASS (gst_kms_sink_parent_class)->finalize (object);
686 }
687
688 static void
689 gst_kms_sink_init (GstKMSSink * sink)
690 {
691   sink->fd = -1;
692   gst_kms_sink_reset (sink);
693 }
694
695 static void
696 gst_kms_sink_class_init (GstKMSSinkClass * klass)
697 {
698   GObjectClass *gobject_class;
699   GstElementClass *gstelement_class;
700   GstBaseSinkClass *gstbasesink_class;
701   GstVideoSinkClass *videosink_class;
702
703   gobject_class = (GObjectClass *) klass;
704   gstelement_class = (GstElementClass *) klass;
705   gstbasesink_class = (GstBaseSinkClass *) klass;
706   videosink_class = (GstVideoSinkClass *) klass;
707
708   gobject_class->finalize = gst_kms_sink_finalize;
709   gobject_class->set_property = gst_kms_sink_set_property;
710   gobject_class->get_property = gst_kms_sink_get_property;
711
712   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
713       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
714           "When enabled, reverse caps negotiation (scaling) will respect "
715           "original aspect ratio", FALSE,
716           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
717   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
718       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
719           "The pixel aspect ratio of the device", "1/1",
720           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
721   g_object_class_install_property (gobject_class, PROP_SCALE,
722       g_param_spec_boolean ("scale", "Scale",
723           "When true, scale to render fullscreen", FALSE,
724           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
725   g_object_class_install_property (gobject_class, PROP_CONNECTOR,
726       g_param_spec_int ("connector", "Connector",
727           "DRM connector id", 1, G_MAXINT32, 7,
728           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
729
730   gst_element_class_set_details_simple (gstelement_class,
731       "Video sink", "Sink/Video",
732       "A video sink using the linux kernel mode setting API",
733       "Alessandro Decina <alessandro.d@gmail.com>");
734
735   gst_element_class_add_pad_template (gstelement_class,
736       gst_static_pad_template_get (&gst_kms_sink_template_factory));
737
738   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_kms_sink_setcaps);
739   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_kms_sink_get_times);
740   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_kms_sink_start);
741   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_kms_sink_stop);
742   gstbasesink_class->propose_allocation =
743       GST_DEBUG_FUNCPTR (gst_kms_sink_propose_allocation);
744   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_kms_sink_query);
745
746   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_kms_sink_show_frame);
747 }
748
749 static gboolean
750 plugin_init (GstPlugin * plugin)
751 {
752   if (!gst_element_register (plugin, "kmssink",
753           GST_RANK_SECONDARY, GST_TYPE_KMS_SINK))
754     return FALSE;
755
756   GST_DEBUG_CATEGORY_INIT (gst_debug_kms_sink, "kmssink", 0, "kmssink element");
757   GST_DEBUG_CATEGORY_GET (GST_CAT_MEMORY, "GST_MEMORY");
758
759   return TRUE;
760 }
761
762 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
763     kms, "KMS video output element", plugin_init, VERSION, GST_LICENSE,
764     GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)