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