plugins: use modern GstElement metadata information.
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapipostproc.c
1 /*
2  *  gstvaapipostproc.c - VA-API video postprocessing
3  *
4  *  Copyright (C) 2012-2013 Intel Corporation
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * SECTION:gstvaapipostproc
24  * @short_description: A video postprocessing filter
25  *
26  * vaapipostproc consists in various postprocessing algorithms to be
27  * applied to VA surfaces. So far, only basic bob deinterlacing is
28  * implemented.
29  */
30
31 #include "gst/vaapi/sysdeps.h"
32 #include <gst/video/video.h>
33 #include <gst/video/videocontext.h>
34 #include <gst/vaapi/gstvaapivideometa.h>
35
36 #include "gstvaapipostproc.h"
37 #include "gstvaapipluginutil.h"
38 #include "gstvaapipluginbuffer.h"
39
40 #define GST_PLUGIN_NAME "vaapipostproc"
41 #define GST_PLUGIN_DESC "A video postprocessing filter"
42
43 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc);
44 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
45
46 /* Default templates */
47 static const char gst_vaapipostproc_sink_caps_str[] =
48     GST_VAAPI_SURFACE_CAPS ", "
49     "interlaced = (boolean) { true, false }";
50
51 static const char gst_vaapipostproc_src_caps_str[] =
52     GST_VAAPI_SURFACE_CAPS ", "
53     "interlaced = (boolean) false";
54
55 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
56     GST_STATIC_PAD_TEMPLATE(
57         "sink",
58         GST_PAD_SINK,
59         GST_PAD_ALWAYS,
60         GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
61
62 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
63     GST_STATIC_PAD_TEMPLATE(
64         "src",
65         GST_PAD_SRC,
66         GST_PAD_ALWAYS,
67         GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
68
69 /* GstImplementsInterface interface */
70 static gboolean
71 gst_vaapipostproc_implements_interface_supported(
72     GstImplementsInterface *iface,
73     GType                   type
74 )
75 {
76     return (type == GST_TYPE_VIDEO_CONTEXT);
77 }
78
79 static void
80 gst_vaapipostproc_implements_iface_init(GstImplementsInterfaceClass *iface)
81 {
82     iface->supported = gst_vaapipostproc_implements_interface_supported;
83 }
84
85 /* GstVideoContext interface */
86 static void
87 gst_vaapipostproc_set_video_context(
88     GstVideoContext *context,
89     const gchar     *type,
90     const GValue    *value
91 )
92 {
93     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(context);
94
95     gst_vaapi_set_display(type, value, &postproc->display);
96 }
97
98 static void
99 gst_video_context_interface_init(GstVideoContextInterface *iface)
100 {
101     iface->set_context = gst_vaapipostproc_set_video_context;
102 }
103
104 #define GstVideoContextClass GstVideoContextInterface
105 G_DEFINE_TYPE_WITH_CODE(
106     GstVaapiPostproc,
107     gst_vaapipostproc,
108     GST_TYPE_ELEMENT,
109     G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
110                           gst_vaapipostproc_implements_iface_init);
111     G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
112                           gst_video_context_interface_init))
113
114 enum {
115     PROP_0,
116
117     PROP_DEINTERLACE_MODE,
118     PROP_DEINTERLACE_METHOD,
119 };
120
121 #define DEFAULT_DEINTERLACE_MODE        GST_VAAPI_DEINTERLACE_MODE_AUTO
122 #define DEFAULT_DEINTERLACE_METHOD      GST_VAAPI_DEINTERLACE_METHOD_BOB
123
124 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
125     gst_vaapi_deinterlace_mode_get_type()
126
127 static GType
128 gst_vaapi_deinterlace_mode_get_type(void)
129 {
130     static GType deinterlace_mode_type = 0;
131
132     static const GEnumValue mode_types[] = {
133         { GST_VAAPI_DEINTERLACE_MODE_AUTO,
134           "Auto detection", "auto" },
135         { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
136           "Force deinterlacing", "interlaced" },
137         { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
138           "Never deinterlace", "disabled" },
139         { 0, NULL, NULL },
140     };
141
142     if (!deinterlace_mode_type) {
143         deinterlace_mode_type =
144             g_enum_register_static("GstVaapiDeinterlaceMode", mode_types);
145     }
146     return deinterlace_mode_type;
147 }
148
149 #define GST_VAAPI_TYPE_DEINTERLACE_METHOD \
150     gst_vaapi_deinterlace_method_get_type()
151
152 static GType
153 gst_vaapi_deinterlace_method_get_type(void)
154 {
155     static GType deinterlace_method_type = 0;
156
157     static const GEnumValue method_types[] = {
158         { GST_VAAPI_DEINTERLACE_METHOD_BOB,
159           "Bob deinterlacing", "bob" },
160 #if 0
161         /* VA/VPP */
162         { GST_VAAPI_DEINTERLACE_METHOD_WEAVE,
163           "Weave deinterlacing", "weave" },
164         { GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE,
165           "Motion adaptive deinterlacing", "motion-adaptive" },
166         { GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED,
167           "Motion compensated deinterlacing", "motion-compensated" },
168 #endif
169         { 0, NULL, NULL },
170     };
171
172     if (!deinterlace_method_type) {
173         deinterlace_method_type =
174             g_enum_register_static("GstVaapiDeinterlaceMethod", method_types);
175     }
176     return deinterlace_method_type;
177 }
178
179 static inline GstVaapiPostproc *
180 get_vaapipostproc_from_pad(GstPad *pad)
181 {
182     return GST_VAAPIPOSTPROC(gst_pad_get_parent_element(pad));
183 }
184
185 static inline gboolean
186 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
187 {
188     return gst_vaapi_ensure_display(postproc, GST_VAAPI_DISPLAY_TYPE_ANY,
189         &postproc->display);
190 }
191
192 static gboolean
193 gst_vaapipostproc_create(GstVaapiPostproc *postproc, GstCaps *caps)
194 {
195     if (!gst_vaapipostproc_ensure_display(postproc))
196         return FALSE;
197
198     gst_caps_replace(&postproc->postproc_caps, caps);
199     return TRUE;
200 }
201
202 static void
203 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
204 {
205     gst_caps_replace(&postproc->postproc_caps, NULL);
206
207     g_clear_object(&postproc->display);
208 }
209
210 static gboolean
211 gst_vaapipostproc_reset(GstVaapiPostproc *postproc, GstCaps *caps)
212 {
213     if (postproc->postproc_caps &&
214         gst_caps_is_always_compatible(caps, postproc->postproc_caps))
215         return TRUE;
216
217     gst_vaapipostproc_destroy(postproc);
218     return gst_vaapipostproc_create(postproc, caps);
219 }
220
221 static gboolean
222 gst_vaapipostproc_start(GstVaapiPostproc *postproc)
223 {
224     if (!gst_vaapipostproc_ensure_display(postproc))
225         return FALSE;
226     return TRUE;
227 }
228
229 static gboolean
230 gst_vaapipostproc_stop(GstVaapiPostproc *postproc)
231 {
232     if (postproc->display) {
233         g_object_unref(postproc->display);
234         postproc->display = NULL;
235     }
236     return TRUE;
237 }
238
239 static GstFlowReturn
240 gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf)
241 {
242     GstVaapiVideoMeta *meta;
243     GstVaapiSurfaceProxy *proxy;
244     GstClockTime timestamp;
245     GstFlowReturn ret;
246     GstBuffer *outbuf = NULL;
247     guint outbuf_flags, flags;
248     gboolean tff;
249
250     meta = gst_buffer_get_vaapi_video_meta(buf);
251     if (!meta)
252         goto error_invalid_buffer;
253
254     flags = gst_vaapi_video_meta_get_render_flags(meta);
255
256     /* Deinterlacing disabled, push frame */
257     if (!postproc->deinterlace) {
258         gst_vaapi_video_meta_set_render_flags(meta, flags);
259         ret = gst_pad_push(postproc->srcpad, buf);
260         if (ret != GST_FLOW_OK)
261             goto error_push_buffer;
262         return GST_FLOW_OK;
263     }
264
265     timestamp  = GST_BUFFER_TIMESTAMP(buf);
266     proxy      = gst_vaapi_video_meta_get_surface_proxy(meta);
267     tff        = GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_TFF);
268
269     flags &= ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
270                GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
271
272     /* First field */
273     outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
274     if (!outbuf)
275         goto error_create_buffer;
276
277     meta = gst_buffer_get_vaapi_video_meta(outbuf);
278     outbuf_flags = flags;
279     outbuf_flags |= postproc->deinterlace ? (
280         tff ?
281         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
282         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
283         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
284     gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
285
286     GST_BUFFER_TIMESTAMP(outbuf) = timestamp;
287     GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
288     gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
289     ret = gst_pad_push(postproc->srcpad, outbuf);
290     if (ret != GST_FLOW_OK)
291         goto error_push_buffer;
292
293     /* Second field */
294     outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
295     if (!outbuf)
296         goto error_create_buffer;
297
298     meta = gst_buffer_get_vaapi_video_meta(outbuf);
299     outbuf_flags = flags;
300     outbuf_flags |= postproc->deinterlace ? (
301         tff ?
302         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
303         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
304         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
305     gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
306
307     GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
308     GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
309     gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
310     ret = gst_pad_push(postproc->srcpad, outbuf);
311     if (ret != GST_FLOW_OK)
312         goto error_push_buffer;
313
314     gst_buffer_unref(buf);
315     return GST_FLOW_OK;
316
317     /* ERRORS */
318 error_invalid_buffer:
319     {
320         GST_ERROR("failed to receive a valid video buffer");
321         gst_buffer_unref(buf);
322         return GST_FLOW_UNEXPECTED;
323     }
324 error_create_buffer:
325     {
326         GST_ERROR("failed to create output buffer");
327         gst_buffer_unref(buf);
328         return GST_FLOW_UNEXPECTED;
329     }
330 error_push_buffer:
331     {
332         if (ret != GST_FLOW_WRONG_STATE)
333             GST_ERROR("failed to push output buffer to video sink");
334         gst_buffer_unref(buf);
335         return GST_FLOW_UNEXPECTED;
336     }
337 }
338
339 static gboolean
340 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps)
341 {
342     gint fps_n, fps_d;
343     gboolean interlaced;
344
345     if (!gst_video_parse_caps_framerate(caps, &fps_n, &fps_d))
346         return FALSE;
347     postproc->fps_n = fps_n;
348     postproc->fps_d = fps_d;
349
350     switch (postproc->deinterlace_mode) {
351     case GST_VAAPI_DEINTERLACE_MODE_AUTO:
352         if (!gst_video_format_parse_caps_interlaced(caps, &interlaced))
353             return FALSE;
354         postproc->deinterlace = interlaced;
355         break;
356     case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
357         postproc->deinterlace = TRUE;
358         break;
359     case GST_VAAPI_DEINTERLACE_MODE_DISABLED:
360         postproc->deinterlace = FALSE;
361         break;
362     }
363
364     postproc->field_duration = gst_util_uint64_scale(
365         GST_SECOND,
366         postproc->fps_d,
367         (1 + postproc->deinterlace) * postproc->fps_n
368     );
369
370     gst_caps_replace(&postproc->sinkpad_caps, caps);
371     return TRUE;
372 }
373
374 static gboolean
375 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps)
376 {
377     GstCaps *src_caps;
378     GstStructure *structure;
379     const GValue *v_width, *v_height, *v_par;
380     gint fps_n, fps_d;
381
382     if (postproc->srcpad_caps)
383         src_caps = gst_caps_make_writable(postproc->srcpad_caps);
384     else
385         src_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
386     if (!src_caps)
387         return FALSE;
388     postproc->srcpad_caps = src_caps;
389
390     structure    = gst_caps_get_structure(caps, 0);
391     v_width      = gst_structure_get_value(structure, "width");
392     v_height     = gst_structure_get_value(structure, "height");
393     v_par        = gst_structure_get_value(structure, "pixel-aspect-ratio");
394
395     structure = gst_caps_get_structure(src_caps, 0);
396     if (v_width && v_height) {
397         gst_structure_set_value(structure, "width", v_width);
398         gst_structure_set_value(structure, "height", v_height);
399     }
400     if (v_par)
401         gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
402
403     gst_structure_set(structure, "type", G_TYPE_STRING, "vaapi", NULL);
404     gst_structure_set(structure, "opengl", G_TYPE_BOOLEAN, USE_GLX, NULL);
405
406     if (!postproc->deinterlace)
407         gst_structure_remove_field(structure, "interlaced");
408     else {
409         /* Set double framerate in interlaced mode */
410         if (!gst_util_fraction_multiply(postproc->fps_n, postproc->fps_d,
411                                         2, 1,
412                                         &fps_n, &fps_d))
413             return FALSE;
414
415         gst_structure_set(
416             structure,
417             "interlaced", G_TYPE_BOOLEAN, FALSE,
418             "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
419             NULL
420         );
421     }
422     return gst_pad_set_caps(postproc->srcpad, src_caps);
423 }
424
425 static gboolean
426 gst_vaapipostproc_ensure_allowed_caps(GstVaapiPostproc *postproc)
427 {
428     if (postproc->allowed_caps)
429         return TRUE;
430
431     postproc->allowed_caps =
432         gst_caps_from_string(gst_vaapipostproc_sink_caps_str);
433     if (!postproc->allowed_caps)
434         return FALSE;
435
436     /* XXX: append VA/VPP filters */
437     return TRUE;
438 }
439
440 static GstCaps *
441 gst_vaapipostproc_get_caps(GstPad *pad)
442 {
443     GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
444     GstCaps *out_caps;
445
446     if (gst_vaapipostproc_ensure_allowed_caps(postproc))
447         out_caps = gst_caps_ref(postproc->allowed_caps);
448     else
449         out_caps = gst_caps_new_empty();
450
451     gst_object_unref(postproc);
452     return out_caps;
453 }
454
455 static gboolean
456 gst_vaapipostproc_set_caps(GstPad *pad, GstCaps *caps)
457 {
458     GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
459     gboolean success = FALSE;
460
461     g_return_val_if_fail(pad == postproc->sinkpad, FALSE);
462
463     do {
464         if (!gst_vaapipostproc_update_sink_caps(postproc, caps))
465             break;
466         if (!gst_vaapipostproc_update_src_caps(postproc, caps))
467             break;
468         if (!gst_vaapipostproc_reset(postproc, postproc->sinkpad_caps))
469             break;
470         success = TRUE;
471     } while (0);
472     gst_object_unref(postproc);
473     return success;
474 }
475
476 static GstFlowReturn
477 gst_vaapipostproc_chain(GstPad *pad, GstBuffer *buf)
478 {
479     GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
480     GstFlowReturn ret;
481
482     ret = gst_vaapipostproc_process(postproc, buf);
483     gst_object_unref(postproc);
484     return ret;
485 }
486
487 static gboolean
488 gst_vaapipostproc_sink_event(GstPad *pad, GstEvent *event)
489 {
490     GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
491     gboolean success;
492
493     GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
494
495     /* Propagate event downstream */
496     success = gst_pad_push_event(postproc->srcpad, event);
497     gst_object_unref(postproc);
498     return success;
499 }
500
501 static gboolean
502 gst_vaapipostproc_src_event(GstPad *pad, GstEvent *event)
503 {
504     GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
505     gboolean success;
506
507     GST_DEBUG("handle src event '%s'", GST_EVENT_TYPE_NAME(event));
508
509     /* Propagate event upstream */
510     success = gst_pad_push_event(postproc->sinkpad, event);
511     gst_object_unref(postproc);
512     return success;
513 }
514
515 static gboolean
516 gst_vaapipostproc_query(GstPad *pad, GstQuery *query)
517 {
518     GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
519     gboolean success;
520
521     GST_DEBUG("sharing display %p", postproc->display);
522
523     if (gst_vaapi_reply_to_query(query, postproc->display))
524         success = TRUE;
525     else
526         success = gst_pad_query_default(pad, query);
527
528     gst_object_unref(postproc);
529     return success;
530 }
531
532 static void
533 gst_vaapipostproc_finalize(GObject *object)
534 {
535     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
536
537     gst_vaapipostproc_destroy(postproc);
538
539     gst_caps_replace(&postproc->sinkpad_caps, NULL);
540     gst_caps_replace(&postproc->srcpad_caps,  NULL);
541     gst_caps_replace(&postproc->allowed_caps, NULL);
542
543     G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
544 }
545
546 static void
547 gst_vaapipostproc_set_property(
548     GObject      *object,
549     guint         prop_id,
550     const GValue *value,
551     GParamSpec   *pspec
552 )
553 {
554     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
555
556     switch (prop_id) {
557     case PROP_DEINTERLACE_MODE:
558         postproc->deinterlace_mode = g_value_get_enum(value);
559         break;
560     case PROP_DEINTERLACE_METHOD:
561         postproc->deinterlace_method = g_value_get_enum(value);
562         break;
563     default:
564         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
565         break;
566     }
567 }
568
569 static void
570 gst_vaapipostproc_get_property(
571     GObject    *object,
572     guint       prop_id,
573     GValue     *value,
574     GParamSpec *pspec
575 )
576 {
577     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
578
579     switch (prop_id) {
580     case PROP_DEINTERLACE_MODE:
581         g_value_set_enum(value, postproc->deinterlace_mode);
582         break;
583     case PROP_DEINTERLACE_METHOD:
584         g_value_set_enum(value, postproc->deinterlace_method);
585         break;
586     default:
587         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
588         break;
589     }
590 }
591
592 static GstStateChangeReturn
593 gst_vaapipostproc_change_state(GstElement *element, GstStateChange transition)
594 {
595     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(element);
596     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
597
598     switch (transition) {
599     case GST_STATE_CHANGE_NULL_TO_READY:
600         if (!gst_vaapipostproc_start(postproc))
601             return GST_STATE_CHANGE_FAILURE;
602         break;
603     case GST_STATE_CHANGE_READY_TO_PAUSED:
604         break;
605     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
606         break;
607     default:
608         break;
609     }
610
611     ret = GST_ELEMENT_CLASS(gst_vaapipostproc_parent_class)->change_state(element, transition);
612     if (ret != GST_STATE_CHANGE_SUCCESS)
613         return ret;
614
615     switch (transition) {
616     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
617         break;
618     case GST_STATE_CHANGE_PAUSED_TO_READY:
619         break;
620     case GST_STATE_CHANGE_READY_TO_NULL:
621         if (!gst_vaapipostproc_stop(postproc))
622             return GST_STATE_CHANGE_FAILURE;
623         break;
624     default:
625         break;
626     }
627     return GST_STATE_CHANGE_SUCCESS;
628 }
629
630 static void
631 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
632 {
633     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
634     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
635     GstPadTemplate *pad_template;
636
637     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
638                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
639
640     object_class->finalize      = gst_vaapipostproc_finalize;
641     object_class->set_property  = gst_vaapipostproc_set_property;
642     object_class->get_property  = gst_vaapipostproc_get_property;
643
644     element_class->change_state = gst_vaapipostproc_change_state;
645
646     gst_element_class_set_static_metadata(element_class,
647         "VA-API video postprocessing",
648         "Filter/Converter/Video",
649         GST_PLUGIN_DESC,
650         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
651
652     /* sink pad */
653     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
654     gst_element_class_add_pad_template(element_class, pad_template);
655     gst_object_unref(pad_template);
656
657     /* src pad */
658     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
659     gst_element_class_add_pad_template(element_class, pad_template);
660     gst_object_unref(pad_template);
661
662     /**
663      * GstVaapiPostproc:deinterlace-mode:
664      *
665      * This selects whether the deinterlacing should always be applied or if
666      * they should only be applied on content that has the "interlaced" flag
667      * on the caps.
668      */
669     g_object_class_install_property
670         (object_class,
671          PROP_DEINTERLACE_MODE,
672          g_param_spec_enum("deinterlace",
673                            "Deinterlace",
674                            "Deinterlace mode to use",
675                            GST_VAAPI_TYPE_DEINTERLACE_MODE,
676                            DEFAULT_DEINTERLACE_MODE,
677                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
678
679     /**
680      * GstVaapiPostproc:deinterlace-method:
681      *
682      * This selects the deinterlacing method to apply.
683      */
684     g_object_class_install_property
685         (object_class,
686          PROP_DEINTERLACE_METHOD,
687          g_param_spec_enum("deinterlace-method",
688                            "Deinterlace method",
689                            "Deinterlace method to use",
690                            GST_VAAPI_TYPE_DEINTERLACE_METHOD,
691                            DEFAULT_DEINTERLACE_METHOD,
692                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
693 }
694
695 static void
696 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
697 {
698     GstVaapiPostprocClass *klass = GST_VAAPIPOSTPROC_GET_CLASS(postproc);
699     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
700
701     postproc->allowed_caps              = NULL;
702     postproc->postproc_caps             = NULL;
703     postproc->display                   = NULL;
704     postproc->surface_width             = 0;
705     postproc->surface_height            = 0;
706     postproc->deinterlace               = FALSE;
707     postproc->deinterlace_mode          = DEFAULT_DEINTERLACE_MODE;
708     postproc->deinterlace_method        = DEFAULT_DEINTERLACE_METHOD;
709     postproc->field_duration            = GST_CLOCK_TIME_NONE;
710     postproc->fps_n                     = 0;
711     postproc->fps_d                     = 0;
712
713     /* Pad through which data comes in to the element */
714     postproc->sinkpad = gst_pad_new_from_template(
715         gst_element_class_get_pad_template(element_class, "sink"),
716         "sink"
717     );
718     postproc->sinkpad_caps = NULL;
719
720     gst_pad_set_getcaps_function(postproc->sinkpad, gst_vaapipostproc_get_caps);
721     gst_pad_set_setcaps_function(postproc->sinkpad, gst_vaapipostproc_set_caps);
722     gst_pad_set_chain_function(postproc->sinkpad, gst_vaapipostproc_chain);
723     gst_pad_set_event_function(postproc->sinkpad, gst_vaapipostproc_sink_event);
724     gst_pad_set_query_function(postproc->sinkpad, gst_vaapipostproc_query);
725     gst_element_add_pad(GST_ELEMENT(postproc), postproc->sinkpad);
726
727     /* Pad through which data goes out of the element */
728     postproc->srcpad = gst_pad_new_from_template(
729         gst_element_class_get_pad_template(element_class, "src"),
730         "src"
731     );
732     postproc->srcpad_caps = NULL;
733
734     gst_pad_set_event_function(postproc->srcpad, gst_vaapipostproc_src_event);
735     gst_pad_set_query_function(postproc->srcpad, gst_vaapipostproc_query);
736     gst_element_add_pad(GST_ELEMENT(postproc), postproc->srcpad);
737 }