pkgconfig: plugin dir should use PKG version not API version.
[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  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapipostproc
25  * @short_description: A video postprocessing filter
26  *
27  * vaapipostproc consists in various postprocessing algorithms to be
28  * applied to VA surfaces. So far, only basic bob deinterlacing is
29  * implemented.
30  */
31
32 #include "gst/vaapi/sysdeps.h"
33 #include <gst/video/video.h>
34
35 #include "gstvaapipostproc.h"
36 #include "gstvaapipluginutil.h"
37 #include "gstvaapivideobuffer.h"
38 #if GST_CHECK_VERSION(1,0,0)
39 #include "gstvaapivideobufferpool.h"
40 #include "gstvaapivideomemory.h"
41 #endif
42
43 #define GST_PLUGIN_NAME "vaapipostproc"
44 #define GST_PLUGIN_DESC "A video postprocessing filter"
45
46 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc);
47 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
48
49 /* Default templates */
50 static const char gst_vaapipostproc_sink_caps_str[] =
51 #if GST_CHECK_VERSION(1,1,0)
52     GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
53         GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ", "
54 #else
55     GST_VAAPI_SURFACE_CAPS ", "
56 #endif
57     GST_CAPS_INTERLACED_MODES "; "
58 #if GST_CHECK_VERSION(1,0,0)
59     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
60 #else
61     "video/x-raw-yuv, "
62     "width  = " GST_VIDEO_SIZE_RANGE ", "
63     "height = " GST_VIDEO_SIZE_RANGE ", "
64     "framerate = " GST_VIDEO_FPS_RANGE ", "
65 #endif
66     GST_CAPS_INTERLACED_MODES;
67
68 static const char gst_vaapipostproc_src_caps_str[] =
69 #if GST_CHECK_VERSION(1,1,0)
70     GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
71         GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ", "
72 #else
73     GST_VAAPI_SURFACE_CAPS ", "
74 #endif
75     GST_CAPS_INTERLACED_FALSE;
76
77 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
78     GST_STATIC_PAD_TEMPLATE(
79         "sink",
80         GST_PAD_SINK,
81         GST_PAD_ALWAYS,
82         GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
83
84 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
85     GST_STATIC_PAD_TEMPLATE(
86         "src",
87         GST_PAD_SRC,
88         GST_PAD_ALWAYS,
89         GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
90
91 G_DEFINE_TYPE_WITH_CODE(
92     GstVaapiPostproc,
93     gst_vaapipostproc,
94     GST_TYPE_BASE_TRANSFORM,
95     GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES)
96
97 enum {
98     PROP_0,
99
100     PROP_FORMAT,
101     PROP_WIDTH,
102     PROP_HEIGHT,
103     PROP_FORCE_ASPECT_RATIO,
104     PROP_DEINTERLACE_MODE,
105     PROP_DEINTERLACE_METHOD,
106     PROP_DENOISE,
107     PROP_SHARPEN,
108 };
109
110 #define DEFAULT_FORMAT                  GST_VIDEO_FORMAT_ENCODED
111 #define DEFAULT_DEINTERLACE_MODE        GST_VAAPI_DEINTERLACE_MODE_AUTO
112 #define DEFAULT_DEINTERLACE_METHOD      GST_VAAPI_DEINTERLACE_METHOD_BOB
113
114 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
115     gst_vaapi_deinterlace_mode_get_type()
116
117 static GType
118 gst_vaapi_deinterlace_mode_get_type(void)
119 {
120     static GType deinterlace_mode_type = 0;
121
122     static const GEnumValue mode_types[] = {
123         { GST_VAAPI_DEINTERLACE_MODE_AUTO,
124           "Auto detection", "auto" },
125         { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
126           "Force deinterlacing", "interlaced" },
127         { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
128           "Never deinterlace", "disabled" },
129         { 0, NULL, NULL },
130     };
131
132     if (!deinterlace_mode_type) {
133         deinterlace_mode_type =
134             g_enum_register_static("GstVaapiDeinterlaceMode", mode_types);
135     }
136     return deinterlace_mode_type;
137 }
138
139 static void
140 ds_reset(GstVaapiDeinterlaceState *ds)
141 {
142     guint i;
143
144     for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++)
145         gst_buffer_replace(&ds->buffers[i], NULL);
146     ds->buffers_index = 0;
147     ds->num_surfaces = 0;
148     ds->deint = FALSE;
149     ds->tff = FALSE;
150 }
151
152 static void
153 ds_add_buffer(GstVaapiDeinterlaceState *ds, GstBuffer *buf)
154 {
155     gst_buffer_replace(&ds->buffers[ds->buffers_index], buf);
156     ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS(ds->buffers);
157 }
158
159 static inline GstBuffer *
160 ds_get_buffer(GstVaapiDeinterlaceState *ds, guint index)
161 {
162     /* Note: the index increases towards older buffers.
163        i.e. buffer at index 0 means the immediately preceding buffer
164        in the history, buffer at index 1 means the one preceding the
165        surface at index 0, etc. */
166     const guint n = ds->buffers_index + G_N_ELEMENTS(ds->buffers) - index - 1;
167     return ds->buffers[n % G_N_ELEMENTS(ds->buffers)];
168 }
169
170 static void
171 ds_set_surfaces(GstVaapiDeinterlaceState *ds)
172 {
173     GstVaapiVideoMeta *meta;
174     guint i;
175
176     ds->num_surfaces = 0;
177     for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++) {
178         GstBuffer * const buf = ds_get_buffer(ds, i);
179         if (!buf)
180             break;
181
182         meta = gst_buffer_get_vaapi_video_meta(buf);
183         ds->surfaces[ds->num_surfaces++] =
184             gst_vaapi_video_meta_get_surface(meta);
185     }
186 }
187
188 static GstVaapiFilterOpInfo *
189 find_filter_op(GPtrArray *filter_ops, GstVaapiFilterOp op)
190 {
191     guint i;
192
193     if (filter_ops) {
194         for (i = 0; i < filter_ops->len; i++) {
195             GstVaapiFilterOpInfo * const filter_op =
196                 g_ptr_array_index(filter_ops, i);
197             if (filter_op->op == op)
198                 return filter_op;
199         }
200     }
201     return NULL;
202 }
203
204 static inline gboolean
205 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
206 {
207     return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(postproc));
208 }
209
210 static gboolean
211 gst_vaapipostproc_ensure_uploader(GstVaapiPostproc *postproc)
212 {
213     if (!gst_vaapipostproc_ensure_display(postproc))
214         return FALSE;
215     if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(postproc)))
216         return FALSE;
217     return TRUE;
218 }
219
220 static gboolean
221 gst_vaapipostproc_ensure_filter(GstVaapiPostproc *postproc)
222 {
223     if (postproc->filter)
224         return TRUE;
225
226     if (!gst_vaapipostproc_ensure_display(postproc))
227         return FALSE;
228
229     postproc->filter = gst_vaapi_filter_new(
230         GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
231     if (!postproc->filter)
232         return FALSE;
233     return TRUE;
234 }
235
236 static gboolean
237 gst_vaapipostproc_ensure_filter_caps(GstVaapiPostproc *postproc)
238 {
239     if (!gst_vaapipostproc_ensure_filter(postproc))
240         return FALSE;
241
242     postproc->filter_ops = gst_vaapi_filter_get_operations(postproc->filter);
243     if (!postproc->filter_ops)
244         return FALSE;
245
246     postproc->filter_formats = gst_vaapi_filter_get_formats(postproc->filter);
247     if (!postproc->filter_formats)
248         return FALSE;
249     return TRUE;
250 }
251
252 static gboolean
253 gst_vaapipostproc_create(GstVaapiPostproc *postproc)
254 {
255     if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
256         return FALSE;
257     if (!gst_vaapipostproc_ensure_display(postproc))
258         return FALSE;
259     if (!gst_vaapipostproc_ensure_uploader(postproc))
260         return FALSE;
261     if (gst_vaapipostproc_ensure_filter(postproc))
262         postproc->use_vpp = TRUE;
263     return TRUE;
264 }
265
266 static void
267 gst_vaapipostproc_destroy_filter(GstVaapiPostproc *postproc)
268 {
269     if (postproc->filter_formats) {
270         g_array_unref(postproc->filter_formats);
271         postproc->filter_formats = NULL;
272     }
273
274     if (postproc->filter_ops) {
275         g_ptr_array_unref(postproc->filter_ops);
276         postproc->filter_ops = NULL;
277     }
278     gst_vaapi_filter_replace(&postproc->filter, NULL);
279     gst_vaapi_video_pool_replace(&postproc->filter_pool, NULL);
280 }
281
282 static void
283 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
284 {
285     ds_reset(&postproc->deinterlace_state);
286     gst_vaapipostproc_destroy_filter(postproc);
287
288     gst_caps_replace(&postproc->allowed_sinkpad_caps, NULL);
289     gst_caps_replace(&postproc->allowed_srcpad_caps, NULL);
290     gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
291 }
292
293 static gboolean
294 gst_vaapipostproc_start(GstBaseTransform *trans)
295 {
296     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
297
298     ds_reset(&postproc->deinterlace_state);
299     if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
300         return FALSE;
301     if (!gst_vaapipostproc_ensure_display(postproc))
302         return FALSE;
303     return TRUE;
304 }
305
306 static gboolean
307 gst_vaapipostproc_stop(GstBaseTransform *trans)
308 {
309     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
310
311     ds_reset(&postproc->deinterlace_state);
312     gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
313     return TRUE;
314 }
315
316 static gboolean
317 is_interlaced_buffer(GstVaapiPostproc *postproc, GstBuffer *buf)
318 {
319     if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
320         return FALSE;
321
322     switch (GST_VIDEO_INFO_INTERLACE_MODE(&postproc->sinkpad_info)) {
323     case GST_VIDEO_INTERLACE_MODE_MIXED:
324 #if GST_CHECK_VERSION(1,0,0)
325         if (!GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
326             return FALSE;
327 #else
328         if (GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_PROGRESSIVE))
329             return FALSE;
330 #endif
331         break;
332     default:
333         break;
334     }
335     return TRUE;
336 }
337
338 static GstBuffer *
339 create_output_buffer(GstVaapiPostproc *postproc)
340 {
341     GstBuffer *outbuf;
342
343     /* Create a raw VA video buffer without GstVaapiVideoMeta attached
344        to it yet, as this will be done next in the transform() hook */
345     outbuf = gst_vaapi_video_buffer_new_empty();
346     if (!outbuf)
347         goto error_create_buffer;
348
349 #if !GST_CHECK_VERSION(1,0,0)
350     gst_buffer_set_caps(outbuf, GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAPS(postproc));
351 #endif
352     return outbuf;
353
354     /* ERRORS */
355 error_create_buffer:
356     {
357         GST_ERROR("failed to create output video buffer");
358         return NULL;
359     }
360 }
361
362 static inline void
363 append_output_buffer_metadata(GstBuffer *outbuf, GstBuffer *inbuf, guint flags)
364 {
365     gst_buffer_copy_into(outbuf, inbuf, flags |
366         GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY,
367         0, -1);
368 }
369
370 static gboolean
371 deint_method_is_advanced(GstVaapiDeinterlaceMethod deint_method)
372 {
373     gboolean is_advanced;
374
375     switch (deint_method) {
376     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
377     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
378         is_advanced = TRUE;
379         break;
380     default:
381         is_advanced = FALSE;
382         break;
383     }
384     return is_advanced;
385 }
386
387 static GstVaapiDeinterlaceMethod
388 get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
389 {
390     switch (deint_method) {
391     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
392         deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
393         break;
394     default:
395         /* Default to basic "bob" for all others */
396         deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
397         break;
398     }
399     return deint_method;
400 }
401
402 static gboolean
403 set_best_deint_method(GstVaapiPostproc *postproc, guint flags,
404     GstVaapiDeinterlaceMethod *deint_method_ptr)
405 {
406     GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
407     gboolean success;
408
409     for (;;) {
410         success = gst_vaapi_filter_set_deinterlacing(postproc->filter,
411             deint_method, flags);
412         if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
413             break;
414         deint_method = get_next_deint_method(deint_method);
415     }
416     *deint_method_ptr = deint_method;
417     return success;
418 }
419
420 static GstFlowReturn
421 gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
422     GstBuffer *outbuf)
423 {
424     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
425     GstVaapiDeinterlaceState * const ds = &postproc->deinterlace_state;
426     GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
427     GstVaapiSurface *inbuf_surface, *outbuf_surface;
428     GstVaapiFilterStatus status;
429     GstClockTime timestamp;
430     GstFlowReturn ret;
431     GstBuffer *fieldbuf;
432     GstVaapiDeinterlaceMethod deint_method;
433     guint flags, deint_flags;
434     gboolean tff, deint, deint_refs, deint_changed;
435
436     /* Validate filters */
437     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
438         !gst_vaapi_filter_set_format(postproc->filter, postproc->format))
439         return GST_FLOW_NOT_SUPPORTED;
440
441     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) &&
442         !gst_vaapi_filter_set_denoising_level(postproc->filter,
443             postproc->denoise_level))
444         return GST_FLOW_NOT_SUPPORTED;
445
446     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) &&
447         !gst_vaapi_filter_set_denoising_level(postproc->filter,
448             postproc->sharpen_level))
449         return GST_FLOW_NOT_SUPPORTED;
450
451     inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
452     if (!inbuf_meta)
453         goto error_invalid_buffer;
454     inbuf_surface = gst_vaapi_video_meta_get_surface(inbuf_meta);
455
456     timestamp  = GST_BUFFER_TIMESTAMP(inbuf);
457     tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
458     deint      = is_interlaced_buffer(postproc, inbuf);
459
460     /* Drop references if deinterlacing conditions changed */
461     deint_changed = deint != ds->deint;
462     if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
463         ds_reset(ds);
464     ds->deint = deint;
465     ds->tff = tff;
466
467     flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
468         ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
469           GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
470
471     /* First field */
472     deint_method = postproc->deinterlace_method;
473     deint_refs = deint_method_is_advanced(deint_method);
474     if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
475         fieldbuf = create_output_buffer(postproc);
476         if (!fieldbuf)
477             goto error_create_buffer;
478
479         outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
480         if (!outbuf_meta)
481             goto error_create_meta;
482         outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
483
484         if (deint) {
485             deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
486             if (tff)
487                 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
488             if (!set_best_deint_method(postproc, deint_flags, &deint_method))
489                 goto error_op_deinterlace;
490
491             if (deint_method != postproc->deinterlace_method) {
492                 GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
493                           postproc->deinterlace_method, deint_method);
494                 postproc->deinterlace_method = deint_method;
495                 deint_refs = deint_method_is_advanced(deint_method);
496             }
497
498             if (deint_refs) {
499                 ds_set_surfaces(ds);
500                 if (!gst_vaapi_filter_set_deinterlacing_references(
501                         postproc->filter, ds->surfaces, ds->num_surfaces,
502                         NULL, 0))
503                     goto error_op_deinterlace;
504             }
505         }
506         else if (deint_changed) {
507             // Reset internal filter to non-deinterlacing mode
508             deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
509             if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
510                     deint_method, 0))
511                 goto error_op_deinterlace;
512         }
513
514         status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
515             outbuf_surface, flags);
516         if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
517             goto error_process_vpp;
518
519         gst_buffer_set_vaapi_video_meta(fieldbuf, outbuf_meta);
520         gst_vaapi_video_meta_unref(outbuf_meta);
521
522         GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
523         GST_BUFFER_DURATION(fieldbuf)  = postproc->field_duration;
524         ret = gst_pad_push(trans->srcpad, fieldbuf);
525         if (ret != GST_FLOW_OK)
526             goto error_push_buffer;
527     }
528     fieldbuf = NULL;
529
530     /* Second field */
531     outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
532     if (!outbuf_meta)
533         goto error_create_meta;
534     outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
535
536     if (deint) {
537         deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
538         if (tff)
539             deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
540         if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
541                 deint_method, deint_flags))
542             goto error_op_deinterlace;
543
544         if (deint_refs && !gst_vaapi_filter_set_deinterlacing_references(
545                 postproc->filter, ds->surfaces, ds->num_surfaces, NULL, 0))
546             goto error_op_deinterlace;
547     }
548     else if (deint_changed && !gst_vaapi_filter_set_deinterlacing(
549                  postproc->filter, deint_method, 0))
550         goto error_op_deinterlace;
551
552     status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
553         outbuf_surface, flags);
554     if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
555         goto error_process_vpp;
556
557     if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
558         gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
559     else {
560         GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
561         GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
562     }
563     gst_buffer_set_vaapi_video_meta(outbuf, outbuf_meta);
564     gst_vaapi_video_meta_unref(outbuf_meta);
565
566     if (deint && deint_refs)
567         ds_add_buffer(ds, inbuf);
568     return GST_FLOW_OK;
569
570     /* ERRORS */
571 error_invalid_buffer:
572     {
573         GST_ERROR("failed to validate source buffer");
574         return GST_FLOW_ERROR;
575     }
576 error_create_buffer:
577     {
578         GST_ERROR("failed to create output buffer");
579         return GST_FLOW_ERROR;
580     }
581 error_create_meta:
582     {
583         GST_ERROR("failed to create new output buffer meta");
584         gst_buffer_replace(&fieldbuf, NULL);
585         gst_vaapi_video_meta_unref(outbuf_meta);
586         return GST_FLOW_ERROR;
587     }
588 error_op_deinterlace:
589     {
590         GST_ERROR("failed to apply deinterlacing filter");
591         gst_buffer_replace(&fieldbuf, NULL);
592         gst_vaapi_video_meta_unref(outbuf_meta);
593         return GST_FLOW_NOT_SUPPORTED;
594     }
595 error_process_vpp:
596     {
597         GST_ERROR("failed to apply VPP filters (error %d)", status);
598         gst_buffer_replace(&fieldbuf, NULL);
599         gst_vaapi_video_meta_unref(outbuf_meta);
600         return GST_FLOW_ERROR;
601     }
602 error_push_buffer:
603     {
604         if (ret != GST_FLOW_FLUSHING)
605             GST_ERROR("failed to push output buffer to video sink");
606         return GST_FLOW_ERROR;
607     }
608 }
609
610 static GstFlowReturn
611 gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
612     GstBuffer *outbuf)
613 {
614     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
615     GstVaapiVideoMeta *meta;
616     GstClockTime timestamp;
617     GstFlowReturn ret;
618     GstBuffer *fieldbuf;
619     guint fieldbuf_flags, outbuf_flags, flags;
620     gboolean tff, deint;
621
622     meta = gst_buffer_get_vaapi_video_meta(inbuf);
623     if (!meta)
624         goto error_invalid_buffer;
625
626     timestamp  = GST_BUFFER_TIMESTAMP(inbuf);
627     tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
628     deint      = is_interlaced_buffer(postproc, inbuf);
629
630     flags = gst_vaapi_video_meta_get_render_flags(meta) &
631         ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
632           GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
633
634     /* First field */
635     fieldbuf = create_output_buffer(postproc);
636     if (!fieldbuf)
637         goto error_create_buffer;
638     append_output_buffer_metadata(fieldbuf, inbuf, 0);
639
640     meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
641     fieldbuf_flags = flags;
642     fieldbuf_flags |= deint ? (
643         tff ?
644         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
645         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
646         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
647     gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
648
649     GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
650     GST_BUFFER_DURATION(fieldbuf)  = postproc->field_duration;
651     ret = gst_pad_push(trans->srcpad, fieldbuf);
652     if (ret != GST_FLOW_OK)
653         goto error_push_buffer;
654
655     /* Second field */
656     append_output_buffer_metadata(outbuf, inbuf, 0);
657
658     meta = gst_buffer_get_vaapi_video_meta(outbuf);
659     outbuf_flags = flags;
660     outbuf_flags |= deint ? (
661         tff ?
662         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
663         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
664         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
665     gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
666
667     GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
668     GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
669     return GST_FLOW_OK;
670
671     /* ERRORS */
672 error_invalid_buffer:
673     {
674         GST_ERROR("failed to validate source buffer");
675         return GST_FLOW_ERROR;
676     }
677 error_create_buffer:
678     {
679         GST_ERROR("failed to create output buffer");
680         return GST_FLOW_EOS;
681     }
682 error_push_buffer:
683     {
684         if (ret != GST_FLOW_FLUSHING)
685             GST_ERROR("failed to push output buffer to video sink");
686         return GST_FLOW_EOS;
687     }
688 }
689
690 static GstFlowReturn
691 gst_vaapipostproc_passthrough(GstBaseTransform *trans, GstBuffer *inbuf,
692     GstBuffer *outbuf)
693 {
694     GstVaapiVideoMeta *meta;
695
696     /* No video processing needed, simply copy buffer metadata */
697     meta = gst_buffer_get_vaapi_video_meta(inbuf);
698     if (!meta)
699         goto error_invalid_buffer;
700
701     append_output_buffer_metadata(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
702     return GST_FLOW_OK;
703
704     /* ERRORS */
705 error_invalid_buffer:
706     {
707         GST_ERROR("failed to validate source buffer");
708         return GST_FLOW_ERROR;
709     }
710 }
711
712 static gboolean
713 is_deinterlace_enabled(GstVaapiPostproc *postproc, GstVideoInfo *vip)
714 {
715     gboolean deinterlace;
716
717     switch (postproc->deinterlace_mode) {
718     case GST_VAAPI_DEINTERLACE_MODE_AUTO:
719         deinterlace = GST_VIDEO_INFO_IS_INTERLACED(vip);
720         break;
721     case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
722         deinterlace = TRUE;
723         break;
724     default:
725         deinterlace = FALSE;
726         break;
727     }
728     return deinterlace;
729 }
730
731 static gboolean
732 video_info_changed(GstVideoInfo *old_vip, GstVideoInfo *new_vip)
733 {
734     if (GST_VIDEO_INFO_FORMAT(old_vip) != GST_VIDEO_INFO_FORMAT(new_vip))
735         return TRUE;
736     if (GST_VIDEO_INFO_INTERLACE_MODE(old_vip) !=
737         GST_VIDEO_INFO_INTERLACE_MODE(new_vip))
738         return TRUE;
739     if (GST_VIDEO_INFO_WIDTH(old_vip) != GST_VIDEO_INFO_WIDTH(new_vip))
740         return TRUE;
741     if (GST_VIDEO_INFO_HEIGHT(old_vip) != GST_VIDEO_INFO_HEIGHT(new_vip))
742         return TRUE;
743     return FALSE;
744 }
745
746 static gboolean
747 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
748     gboolean *caps_changed_ptr)
749 {
750     GstVideoInfo vi;
751     gboolean deinterlace;
752
753     if (!gst_video_info_from_caps(&vi, caps))
754         return FALSE;
755
756     if (video_info_changed(&vi, &postproc->sinkpad_info))
757         postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
758
759     deinterlace = is_deinterlace_enabled(postproc, &vi);
760     if (deinterlace)
761         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
762     postproc->field_duration = gst_util_uint64_scale(
763         GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
764         (1 + deinterlace) * GST_VIDEO_INFO_FPS_N(&vi));
765
766     postproc->is_raw_yuv = GST_VIDEO_INFO_IS_YUV(&vi);
767     return TRUE;
768 }
769
770 static gboolean
771 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps,
772     gboolean *caps_changed_ptr)
773 {
774     GstVideoInfo vi;
775
776     if (!gst_video_info_from_caps(&vi, caps))
777         return FALSE;
778
779     if (video_info_changed(&vi, &postproc->srcpad_info))
780         postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
781
782     if (postproc->format != GST_VIDEO_INFO_FORMAT(&postproc->sinkpad_info))
783         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
784
785     if ((postproc->width || postproc->height) &&
786         postproc->width != GST_VIDEO_INFO_WIDTH(&postproc->sinkpad_info) &&
787         postproc->height != GST_VIDEO_INFO_HEIGHT(&postproc->sinkpad_info))
788         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
789     return TRUE;
790 }
791
792 static gboolean
793 ensure_allowed_sinkpad_caps(GstVaapiPostproc *postproc)
794 {
795     GstCaps *out_caps, *yuv_caps;
796
797     if (postproc->allowed_sinkpad_caps)
798         return TRUE;
799
800     /* Create VA caps */
801 #if GST_CHECK_VERSION(1,1,0)
802     out_caps = gst_static_pad_template_get_caps(
803         &gst_vaapipostproc_sink_factory);
804 #else
805     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS ", "
806         GST_CAPS_INTERLACED_MODES);
807 #endif
808     if (!out_caps) {
809         GST_ERROR("failed to create VA sink caps");
810         return FALSE;
811     }
812
813     /* Append YUV caps */
814     if (gst_vaapipostproc_ensure_uploader(postproc)) {
815         yuv_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(postproc);
816         if (yuv_caps) {
817             out_caps = gst_caps_make_writable(out_caps);
818             gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
819         }
820         else
821             GST_WARNING("failed to create YUV sink caps");
822     }
823     postproc->allowed_sinkpad_caps = out_caps;
824
825     /* XXX: append VA/VPP filters */
826     return TRUE;
827 }
828
829 /* Fixup output caps so that to reflect the supported set of pixel formats */
830 static GstCaps *
831 expand_allowed_srcpad_caps(GstVaapiPostproc *postproc, GstCaps *caps)
832 {
833     GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
834     guint i, num_structures;
835     gboolean had_filter;
836
837     had_filter = postproc->filter != NULL;
838     if (!had_filter && !gst_vaapipostproc_ensure_filter(postproc))
839         goto cleanup;
840     if (!gst_vaapipostproc_ensure_filter_caps(postproc))
841         goto cleanup;
842
843     /* Reset "format" field for each structure */
844     if (!gst_vaapi_value_set_format_list(&value, postproc->filter_formats))
845         goto cleanup;
846     if (gst_vaapi_value_set_format(&v_format, GST_VIDEO_FORMAT_ENCODED)) {
847         gst_value_list_prepend_value(&value, &v_format);
848         g_value_unset(&v_format);
849     }
850
851     num_structures = gst_caps_get_size(caps);
852     for (i = 0; i < num_structures; i++) {
853         GstStructure * const structure = gst_caps_get_structure(caps, i);
854         if (!structure)
855             continue;
856         gst_structure_set_value(structure, "format", &value);
857     }
858     g_value_unset(&value);
859
860 cleanup:
861     if (!had_filter)
862         gst_vaapipostproc_destroy_filter(postproc);
863     return caps;
864 }
865
866 static gboolean
867 ensure_allowed_srcpad_caps(GstVaapiPostproc *postproc)
868 {
869     GstCaps *out_caps;
870
871     if (postproc->allowed_srcpad_caps)
872         return TRUE;
873
874     /* Create initial caps from pad template */
875     out_caps = gst_caps_from_string(gst_vaapipostproc_src_caps_str);
876     if (!out_caps) {
877         GST_ERROR("failed to create VA src caps");
878         return FALSE;
879     }
880
881     postproc->allowed_srcpad_caps =
882         expand_allowed_srcpad_caps(postproc, out_caps);
883     return postproc->allowed_srcpad_caps != NULL;
884 }
885
886 static void
887 find_best_size(GstVaapiPostproc *postproc, GstVideoInfo *vip,
888     guint *width_ptr, guint *height_ptr)
889 {
890     guint width, height;
891
892     width  = GST_VIDEO_INFO_WIDTH(vip);
893     height = GST_VIDEO_INFO_HEIGHT(vip);
894     if (postproc->width && postproc->height) {
895         width = postproc->width;
896         height = postproc->height;
897     }
898     else if (postproc->keep_aspect) {
899         const gdouble ratio  = (gdouble)width / height;
900         if (postproc->width) {
901             width = postproc->width;
902             height = postproc->width / ratio;
903         }
904         else if (postproc->height) {
905             height = postproc->height;
906             width = postproc->height * ratio;
907         }
908     }
909     else if (postproc->width)
910         width = postproc->width;
911     else if (postproc->height)
912         height = postproc->height;
913
914     *width_ptr = width;
915     *height_ptr = height;
916 }
917
918 static GstCaps *
919 gst_vaapipostproc_transform_caps_impl(GstBaseTransform *trans,
920     GstPadDirection direction, GstCaps *caps)
921 {
922     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
923     GstVideoInfo vi;
924     GstVideoFormat format;
925     GstCaps *out_caps;
926     guint width, height;
927
928     /* Generate the sink pad caps, that could be fixated afterwards */
929     if (direction == GST_PAD_SRC) {
930         if (!ensure_allowed_sinkpad_caps(postproc))
931             return NULL;
932         return gst_caps_ref(postproc->allowed_sinkpad_caps);
933     }
934
935     /* Generate complete set of src pad caps if non-fixated sink pad
936        caps are provided */
937     if (!gst_caps_is_fixed(caps)) {
938         if (!ensure_allowed_srcpad_caps(postproc))
939             return NULL;
940         return gst_caps_ref(postproc->allowed_srcpad_caps);
941     }
942
943     /* Generate the expected src pad caps, from the current fixated
944        sink pad caps */
945     if (!gst_video_info_from_caps(&vi, caps))
946         return NULL;
947
948     // Set double framerate in interlaced mode
949     if (is_deinterlace_enabled(postproc, &vi)) {
950         gint fps_n = GST_VIDEO_INFO_FPS_N(&vi);
951         gint fps_d = GST_VIDEO_INFO_FPS_D(&vi);
952         if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
953             return NULL;
954         GST_VIDEO_INFO_FPS_N(&vi) = fps_n;
955         GST_VIDEO_INFO_FPS_D(&vi) = fps_d;
956     }
957
958     // Signal the other pad that we only generate progressive frames
959     GST_VIDEO_INFO_INTERLACE_MODE(&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
960
961     // Update size from user-specified parameters
962 #if GST_CHECK_VERSION(1,1,0)
963     format = postproc->format;
964 #else
965     format = GST_VIDEO_FORMAT_ENCODED;
966 #endif
967     find_best_size(postproc, &vi, &width, &height);
968     gst_video_info_set_format(&vi, format, width, height);
969
970 #if GST_CHECK_VERSION(1,1,0)
971     out_caps = gst_video_info_to_caps(&vi);
972     if (!out_caps)
973         return NULL;
974
975     gst_caps_set_features(out_caps, 0,
976         gst_caps_features_new(GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, NULL));
977 #else
978     /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
979        reconstruct suitable caps for "encoded" video formats */
980     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
981     if (!out_caps)
982         return NULL;
983
984     gst_caps_set_simple(out_caps,
985         "type", G_TYPE_STRING, "vaapi",
986         "opengl", G_TYPE_BOOLEAN, USE_GLX,
987         "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH(&vi),
988         "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT(&vi),
989         "framerate", GST_TYPE_FRACTION, GST_VIDEO_INFO_FPS_N(&vi),
990             GST_VIDEO_INFO_FPS_D(&vi),
991         "pixel-aspect-ratio", GST_TYPE_FRACTION, GST_VIDEO_INFO_PAR_N(&vi),
992             GST_VIDEO_INFO_PAR_D(&vi),
993         NULL);
994
995     gst_caps_set_interlaced(out_caps, &vi);
996 #endif
997     return out_caps;
998 }
999
1000 #if GST_CHECK_VERSION(1,0,0)
1001 static GstCaps *
1002 gst_vaapipostproc_transform_caps(GstBaseTransform *trans,
1003     GstPadDirection direction, GstCaps *caps, GstCaps *filter)
1004 {
1005     GstCaps *out_caps;
1006
1007     caps = gst_vaapipostproc_transform_caps_impl(trans, direction, caps);
1008     if (caps && filter) {
1009         out_caps = gst_caps_intersect_full(caps, filter,
1010             GST_CAPS_INTERSECT_FIRST);
1011         gst_caps_unref(caps);
1012         return out_caps;
1013     }
1014     return caps;
1015 }
1016 #else
1017 #define gst_vaapipostproc_transform_caps \
1018     gst_vaapipostproc_transform_caps_impl
1019 #endif
1020
1021 #if GST_CHECK_VERSION(1,0,0)
1022 typedef gsize GstBaseTransformSizeType;
1023 #else
1024 typedef guint GstBaseTransformSizeType;
1025 #endif
1026
1027 static gboolean
1028 gst_vaapipostproc_transform_size(GstBaseTransform *trans,
1029     GstPadDirection direction, GstCaps *caps, GstBaseTransformSizeType size,
1030     GstCaps *othercaps, GstBaseTransformSizeType *othersize)
1031 {
1032     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1033
1034     if (direction == GST_PAD_SINK || !postproc->is_raw_yuv)
1035         *othersize = 0;
1036     else
1037         *othersize = size;
1038     return TRUE;
1039 }
1040
1041 static GstFlowReturn
1042 gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
1043     GstBuffer *outbuf)
1044 {
1045     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1046     GstBuffer *buf;
1047     GstFlowReturn ret;
1048
1049     ret = gst_vaapi_plugin_base_get_input_buffer(
1050         GST_VAAPI_PLUGIN_BASE(postproc), inbuf, &buf);
1051     if (ret != GST_FLOW_OK)
1052         return GST_FLOW_ERROR;
1053
1054     ret = GST_FLOW_NOT_SUPPORTED;
1055     if (postproc->flags) {
1056         /* Use VA/VPP extensions to process this frame */
1057         if (postproc->use_vpp &&
1058             postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1059             ret = gst_vaapipostproc_process_vpp(trans, buf, outbuf);
1060             if (ret != GST_FLOW_NOT_SUPPORTED)
1061                 goto done;
1062             GST_WARNING("unsupported VPP filters. Disabling");
1063             postproc->use_vpp = FALSE;
1064         }
1065
1066         /* Only append picture structure meta data (top/bottom field) */
1067         if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1068             ret = gst_vaapipostproc_process(trans, buf, outbuf);
1069             if (ret != GST_FLOW_NOT_SUPPORTED)
1070                 goto done;
1071         }
1072     }
1073
1074     /* Fallback: passthrough to the downstream element as is */
1075     ret = gst_vaapipostproc_passthrough(trans, buf, outbuf);
1076
1077 done:
1078     gst_buffer_unref(buf);
1079     return ret;
1080 }
1081
1082 static GstFlowReturn
1083 gst_vaapipostproc_prepare_output_buffer(GstBaseTransform *trans,
1084     GstBuffer *inbuf,
1085 #if !GST_CHECK_VERSION(1,0,0)
1086     gint size, GstCaps *caps,
1087 #endif
1088     GstBuffer **outbuf_ptr)
1089 {
1090     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1091
1092     *outbuf_ptr = create_output_buffer(postproc);
1093     return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
1094 }
1095
1096 static gboolean
1097 ensure_srcpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
1098 {
1099     GstVideoInfo vi;
1100     GstVaapiVideoPool *pool;
1101
1102     gst_video_info_init(&vi);
1103     gst_video_info_from_caps(&vi, caps);
1104     gst_video_info_set_format(&vi, postproc->format,
1105         GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
1106
1107     if (!video_info_changed(&vi, &postproc->filter_pool_info))
1108         return TRUE;
1109     postproc->filter_pool_info = vi;
1110
1111     pool = gst_vaapi_surface_pool_new(GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc),
1112         &postproc->filter_pool_info);
1113     if (!pool)
1114         return FALSE;
1115
1116     gst_vaapi_video_pool_replace(&postproc->filter_pool, pool);
1117     gst_vaapi_video_pool_unref(pool);
1118     return TRUE;
1119 }
1120
1121 static gboolean
1122 gst_vaapipostproc_set_caps(GstBaseTransform *trans, GstCaps *caps,
1123     GstCaps *out_caps)
1124 {
1125     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1126     gboolean caps_changed = FALSE;
1127
1128     if (!gst_vaapipostproc_update_sink_caps(postproc, caps, &caps_changed))
1129         return FALSE;
1130     if (!gst_vaapipostproc_update_src_caps(postproc, out_caps, &caps_changed))
1131         return FALSE;
1132
1133     if (caps_changed) {
1134         gst_vaapipostproc_destroy(postproc);
1135         if (!gst_vaapipostproc_create(postproc))
1136             return FALSE;
1137         if (!gst_vaapi_plugin_base_set_caps(GST_VAAPI_PLUGIN_BASE(trans),
1138                 caps, out_caps))
1139             return FALSE;
1140     }
1141
1142     if (!ensure_srcpad_buffer_pool(postproc, out_caps))
1143         return FALSE;
1144     return TRUE;
1145 }
1146
1147 static gboolean
1148 gst_vaapipostproc_query(GstBaseTransform *trans, GstPadDirection direction,
1149     GstQuery *query)
1150 {
1151     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1152
1153     GST_INFO_OBJECT(trans, "query type `%s'", GST_QUERY_TYPE_NAME(query));
1154
1155     if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc))) {
1156         GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
1157         return TRUE;
1158     }
1159
1160     return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
1161         trans, direction, query);
1162 }
1163
1164 #if GST_CHECK_VERSION(1,0,0)
1165 static gboolean
1166 gst_vaapipostproc_propose_allocation(GstBaseTransform *trans,
1167     GstQuery *decide_query, GstQuery *query)
1168 {
1169     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1170     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(trans);
1171
1172     /* Let vaapidecode allocate the video buffers */
1173     if (!postproc->is_raw_yuv)
1174         return FALSE;
1175     if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1176         return FALSE;
1177     return TRUE;
1178 }
1179 #endif
1180
1181 static void
1182 gst_vaapipostproc_finalize(GObject *object)
1183 {
1184     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1185
1186     gst_vaapipostproc_destroy(postproc);
1187
1188     gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(postproc));
1189     G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
1190 }
1191
1192 static void
1193 gst_vaapipostproc_set_property(
1194     GObject      *object,
1195     guint         prop_id,
1196     const GValue *value,
1197     GParamSpec   *pspec
1198 )
1199 {
1200     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1201
1202     switch (prop_id) {
1203     case PROP_FORMAT:
1204         postproc->format = g_value_get_enum(value);
1205         break;
1206     case PROP_WIDTH:
1207         postproc->width = g_value_get_uint(value);
1208         break;
1209     case PROP_HEIGHT:
1210         postproc->height = g_value_get_uint(value);
1211         break;
1212     case PROP_FORCE_ASPECT_RATIO:
1213         postproc->keep_aspect = g_value_get_boolean(value);
1214         break;
1215     case PROP_DEINTERLACE_MODE:
1216         postproc->deinterlace_mode = g_value_get_enum(value);
1217         break;
1218     case PROP_DEINTERLACE_METHOD:
1219         postproc->deinterlace_method = g_value_get_enum(value);
1220         break;
1221      case PROP_DENOISE:
1222          postproc->denoise_level = g_value_get_float(value);
1223          postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1224          break;
1225      case PROP_SHARPEN:
1226          postproc->sharpen_level = g_value_get_float(value);
1227          postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1228          break;
1229     default:
1230         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1231         break;
1232     }
1233 }
1234
1235 static void
1236 gst_vaapipostproc_get_property(
1237     GObject    *object,
1238     guint       prop_id,
1239     GValue     *value,
1240     GParamSpec *pspec
1241 )
1242 {
1243     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1244
1245     switch (prop_id) {
1246     case PROP_FORMAT:
1247         g_value_set_enum(value, postproc->format);
1248         break;
1249     case PROP_WIDTH:
1250         g_value_set_uint(value, postproc->width);
1251         break;
1252     case PROP_HEIGHT:
1253         g_value_set_uint(value, postproc->height);
1254         break;
1255     case PROP_FORCE_ASPECT_RATIO:
1256         g_value_set_boolean(value, postproc->keep_aspect);
1257         break;
1258     case PROP_DEINTERLACE_MODE:
1259         g_value_set_enum(value, postproc->deinterlace_mode);
1260         break;
1261     case PROP_DEINTERLACE_METHOD:
1262         g_value_set_enum(value, postproc->deinterlace_method);
1263         break;
1264     case PROP_DENOISE:
1265         g_value_set_float(value, postproc->denoise_level);
1266         break;
1267     case PROP_SHARPEN:
1268         g_value_set_float(value, postproc->sharpen_level);
1269         break;
1270     default:
1271         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1272         break;
1273     }
1274 }
1275
1276 static void
1277 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
1278 {
1279     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
1280     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
1281     GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
1282     GstPadTemplate *pad_template;
1283     GPtrArray *filter_ops;
1284     GstVaapiFilterOpInfo *filter_op;
1285
1286     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
1287                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1288
1289     gst_vaapi_plugin_base_class_init(GST_VAAPI_PLUGIN_BASE_CLASS(klass));
1290
1291     object_class->finalize      = gst_vaapipostproc_finalize;
1292     object_class->set_property  = gst_vaapipostproc_set_property;
1293     object_class->get_property  = gst_vaapipostproc_get_property;
1294     trans_class->start          = gst_vaapipostproc_start;
1295     trans_class->stop           = gst_vaapipostproc_stop;
1296     trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1297     trans_class->transform_size = gst_vaapipostproc_transform_size;
1298     trans_class->transform      = gst_vaapipostproc_transform;
1299     trans_class->set_caps       = gst_vaapipostproc_set_caps;
1300     trans_class->query          = gst_vaapipostproc_query;
1301
1302 #if GST_CHECK_VERSION(1,0,0)
1303     trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1304 #endif
1305
1306     trans_class->prepare_output_buffer =
1307         gst_vaapipostproc_prepare_output_buffer;
1308
1309     gst_element_class_set_static_metadata(element_class,
1310         "VA-API video postprocessing",
1311         "Filter/Converter/Video",
1312         GST_PLUGIN_DESC,
1313         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1314
1315     /* sink pad */
1316     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
1317     gst_element_class_add_pad_template(element_class, pad_template);
1318
1319     /* src pad */
1320     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
1321     gst_element_class_add_pad_template(element_class, pad_template);
1322
1323     /**
1324      * GstVaapiPostproc:deinterlace-mode:
1325      *
1326      * This selects whether the deinterlacing should always be applied or if
1327      * they should only be applied on content that has the "interlaced" flag
1328      * on the caps.
1329      */
1330     g_object_class_install_property
1331         (object_class,
1332          PROP_DEINTERLACE_MODE,
1333          g_param_spec_enum("deinterlace-mode",
1334                            "Deinterlace mode",
1335                            "Deinterlace mode to use",
1336                            GST_VAAPI_TYPE_DEINTERLACE_MODE,
1337                            DEFAULT_DEINTERLACE_MODE,
1338                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1339
1340     /**
1341      * GstVaapiPostproc:deinterlace-method:
1342      *
1343      * This selects the deinterlacing method to apply.
1344      */
1345     g_object_class_install_property
1346         (object_class,
1347          PROP_DEINTERLACE_METHOD,
1348          g_param_spec_enum("deinterlace-method",
1349                            "Deinterlace method",
1350                            "Deinterlace method to use",
1351                            GST_VAAPI_TYPE_DEINTERLACE_METHOD,
1352                            DEFAULT_DEINTERLACE_METHOD,
1353                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1354
1355     filter_ops = gst_vaapi_filter_get_operations(NULL);
1356     if (!filter_ops)
1357         return;
1358
1359     /**
1360      * GstVaapiPostproc:format:
1361      *
1362      * The forced output pixel format, expressed as a #GstVideoFormat.
1363      */
1364     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
1365     if (filter_op)
1366         g_object_class_install_property(object_class,
1367             PROP_FORMAT, filter_op->pspec);
1368
1369     /**
1370      * GstVaapiPostproc:width:
1371      *
1372      * The forced output width in pixels. If set to zero, the width is
1373      * calculated from the height if aspect ration is preserved, or
1374      * inherited from the sink caps width
1375      */
1376     g_object_class_install_property
1377         (object_class,
1378          PROP_WIDTH,
1379          g_param_spec_uint("width",
1380                            "Width",
1381                            "Forced output width",
1382                            0, G_MAXINT, 0,
1383                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1384
1385     /**
1386      * GstVaapiPostproc:height:
1387      *
1388      * The forced output height in pixels. If set to zero, the height
1389      * is calculated from the width if aspect ration is preserved, or
1390      * inherited from the sink caps height
1391      */
1392     g_object_class_install_property
1393         (object_class,
1394          PROP_HEIGHT,
1395          g_param_spec_uint("height",
1396                            "Height",
1397                            "Forced output height",
1398                            0, G_MAXINT, 0,
1399                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1400
1401     /**
1402      * GstVaapiPostproc:force-aspect-ratio:
1403      *
1404      * When enabled, scaling respects video aspect ratio; when
1405      * disabled, the video is distorted to fit the width and height
1406      * properties.
1407      */
1408     g_object_class_install_property
1409         (object_class,
1410          PROP_FORCE_ASPECT_RATIO,
1411          g_param_spec_boolean("force-aspect-ratio",
1412                               "Force aspect ratio",
1413                               "When enabled, scaling will respect original aspect ratio",
1414                               TRUE,
1415                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1416
1417     /**
1418      * GstVaapiPostproc:denoise:
1419      *
1420      * The level of noise reduction to apply.
1421      */
1422     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
1423     if (filter_op)
1424         g_object_class_install_property(object_class,
1425             PROP_DENOISE, filter_op->pspec);
1426
1427     /**
1428      * GstVaapiPostproc:sharpen:
1429      *
1430      * The level of sharpening to apply for positive values, or the
1431      * level of blurring for negative values.
1432      */
1433     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
1434     if (filter_op)
1435         g_object_class_install_property(object_class,
1436             PROP_SHARPEN, filter_op->pspec);
1437
1438     g_ptr_array_unref(filter_ops);
1439 }
1440
1441 static void
1442 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
1443 {
1444     gst_vaapi_plugin_base_init(GST_VAAPI_PLUGIN_BASE(postproc), GST_CAT_DEFAULT);
1445
1446     postproc->format                    = DEFAULT_FORMAT;
1447     postproc->deinterlace_mode          = DEFAULT_DEINTERLACE_MODE;
1448     postproc->deinterlace_method        = DEFAULT_DEINTERLACE_METHOD;
1449     postproc->field_duration            = GST_CLOCK_TIME_NONE;
1450     postproc->keep_aspect               = TRUE;
1451
1452     gst_video_info_init(&postproc->sinkpad_info);
1453     gst_video_info_init(&postproc->srcpad_info);
1454     gst_video_info_init(&postproc->filter_pool_info);
1455 }