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