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