vaapipostproc: allow user defined scaling mode.
[platform/upstream/gstreamer.git] / gst / vaapi / gstvaapipostproc.c
1 /*
2  *  gstvaapipostproc.c - VA-API video postprocessing
3  *
4  *  Copyright (C) 2012-2014 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 #if GST_CHECK_VERSION(1,1,0)
50 # define GST_VAAPIPOSTPROC_SURFACE_CAPS \
51     GST_VIDEO_CAPS_MAKE_WITH_FEATURES(  \
52         GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, I420, YV12, NV12 }")
53 #else
54 # define GST_VAAPIPOSTPROC_SURFACE_CAPS \
55     GST_VAAPI_SURFACE_CAPS
56 #endif
57
58 /* Default templates */
59 static const char gst_vaapipostproc_sink_caps_str[] =
60     GST_VAAPIPOSTPROC_SURFACE_CAPS ", "
61     GST_CAPS_INTERLACED_MODES "; "
62 #if GST_CHECK_VERSION(1,0,0)
63     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
64 #else
65     "video/x-raw-yuv, "
66     "width  = " GST_VIDEO_SIZE_RANGE ", "
67     "height = " GST_VIDEO_SIZE_RANGE ", "
68     "framerate = " GST_VIDEO_FPS_RANGE ", "
69 #endif
70     GST_CAPS_INTERLACED_MODES;
71
72 static const char gst_vaapipostproc_src_caps_str[] =
73     GST_VAAPIPOSTPROC_SURFACE_CAPS ", "
74     GST_CAPS_INTERLACED_FALSE "; "
75 #if GST_CHECK_VERSION(1,1,0)
76     GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
77         GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA") ", "
78     GST_CAPS_INTERLACED_FALSE "; "
79 #endif
80 #if GST_CHECK_VERSION(1,0,0)
81     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
82 #else
83     "video/x-raw-yuv, "
84     "width  = " GST_VIDEO_SIZE_RANGE ", "
85     "height = " GST_VIDEO_SIZE_RANGE ", "
86     "framerate = " GST_VIDEO_FPS_RANGE ", "
87 #endif
88     GST_CAPS_INTERLACED_FALSE;
89
90 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
91     GST_STATIC_PAD_TEMPLATE(
92         "sink",
93         GST_PAD_SINK,
94         GST_PAD_ALWAYS,
95         GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
96
97 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
98     GST_STATIC_PAD_TEMPLATE(
99         "src",
100         GST_PAD_SRC,
101         GST_PAD_ALWAYS,
102         GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
103
104 G_DEFINE_TYPE_WITH_CODE(
105     GstVaapiPostproc,
106     gst_vaapipostproc,
107     GST_TYPE_BASE_TRANSFORM,
108     GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES)
109
110 enum {
111     PROP_0,
112
113     PROP_FORMAT,
114     PROP_WIDTH,
115     PROP_HEIGHT,
116     PROP_FORCE_ASPECT_RATIO,
117     PROP_DEINTERLACE_MODE,
118     PROP_DEINTERLACE_METHOD,
119     PROP_DENOISE,
120     PROP_SHARPEN,
121     PROP_HUE,
122     PROP_SATURATION,
123     PROP_BRIGHTNESS,
124     PROP_CONTRAST,
125     PROP_SCALE_METHOD,
126 };
127
128 #define DEFAULT_FORMAT                  GST_VIDEO_FORMAT_ENCODED
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 static void
158 ds_reset(GstVaapiDeinterlaceState *ds)
159 {
160     guint i;
161
162     for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++)
163         gst_buffer_replace(&ds->buffers[i], NULL);
164     ds->buffers_index = 0;
165     ds->num_surfaces = 0;
166     ds->deint = FALSE;
167     ds->tff = FALSE;
168 }
169
170 static void
171 ds_add_buffer(GstVaapiDeinterlaceState *ds, GstBuffer *buf)
172 {
173     gst_buffer_replace(&ds->buffers[ds->buffers_index], buf);
174     ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS(ds->buffers);
175 }
176
177 static inline GstBuffer *
178 ds_get_buffer(GstVaapiDeinterlaceState *ds, guint index)
179 {
180     /* Note: the index increases towards older buffers.
181        i.e. buffer at index 0 means the immediately preceding buffer
182        in the history, buffer at index 1 means the one preceding the
183        surface at index 0, etc. */
184     const guint n = ds->buffers_index + G_N_ELEMENTS(ds->buffers) - index - 1;
185     return ds->buffers[n % G_N_ELEMENTS(ds->buffers)];
186 }
187
188 static void
189 ds_set_surfaces(GstVaapiDeinterlaceState *ds)
190 {
191     GstVaapiVideoMeta *meta;
192     guint i;
193
194     ds->num_surfaces = 0;
195     for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++) {
196         GstBuffer * const buf = ds_get_buffer(ds, i);
197         if (!buf)
198             break;
199
200         meta = gst_buffer_get_vaapi_video_meta(buf);
201         ds->surfaces[ds->num_surfaces++] =
202             gst_vaapi_video_meta_get_surface(meta);
203     }
204 }
205
206 static GstVaapiFilterOpInfo *
207 find_filter_op(GPtrArray *filter_ops, GstVaapiFilterOp op)
208 {
209     guint i;
210
211     if (filter_ops) {
212         for (i = 0; i < filter_ops->len; i++) {
213             GstVaapiFilterOpInfo * const filter_op =
214                 g_ptr_array_index(filter_ops, i);
215             if (filter_op->op == op)
216                 return filter_op;
217         }
218     }
219     return NULL;
220 }
221
222 static inline gboolean
223 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
224 {
225     return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(postproc));
226 }
227
228 static gboolean
229 gst_vaapipostproc_ensure_uploader(GstVaapiPostproc *postproc)
230 {
231     if (!gst_vaapipostproc_ensure_display(postproc))
232         return FALSE;
233     if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(postproc)))
234         return FALSE;
235     return TRUE;
236 }
237
238 static gboolean
239 gst_vaapipostproc_ensure_filter(GstVaapiPostproc *postproc)
240 {
241     if (postproc->filter)
242         return TRUE;
243
244     if (!gst_vaapipostproc_ensure_display(postproc))
245         return FALSE;
246
247     postproc->filter = gst_vaapi_filter_new(
248         GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
249     if (!postproc->filter)
250         return FALSE;
251     return TRUE;
252 }
253
254 static gboolean
255 gst_vaapipostproc_ensure_filter_caps(GstVaapiPostproc *postproc)
256 {
257     if (!gst_vaapipostproc_ensure_filter(postproc))
258         return FALSE;
259
260     postproc->filter_ops = gst_vaapi_filter_get_operations(postproc->filter);
261     if (!postproc->filter_ops)
262         return FALSE;
263
264     postproc->filter_formats = gst_vaapi_filter_get_formats(postproc->filter);
265     if (!postproc->filter_formats)
266         return FALSE;
267     return TRUE;
268 }
269
270 static gboolean
271 gst_vaapipostproc_create(GstVaapiPostproc *postproc)
272 {
273     if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
274         return FALSE;
275     if (!gst_vaapipostproc_ensure_display(postproc))
276         return FALSE;
277     if (!gst_vaapipostproc_ensure_uploader(postproc))
278         return FALSE;
279
280     postproc->use_vpp = FALSE;
281     postproc->has_vpp = gst_vaapipostproc_ensure_filter(postproc);
282     return TRUE;
283 }
284
285 static void
286 gst_vaapipostproc_destroy_filter(GstVaapiPostproc *postproc)
287 {
288     if (postproc->filter_formats) {
289         g_array_unref(postproc->filter_formats);
290         postproc->filter_formats = NULL;
291     }
292
293     if (postproc->filter_ops) {
294         g_ptr_array_unref(postproc->filter_ops);
295         postproc->filter_ops = NULL;
296     }
297     gst_vaapi_filter_replace(&postproc->filter, NULL);
298     gst_vaapi_video_pool_replace(&postproc->filter_pool, NULL);
299     postproc->filter_pool_active = FALSE;
300 }
301
302 static void
303 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
304 {
305     ds_reset(&postproc->deinterlace_state);
306     gst_vaapipostproc_destroy_filter(postproc);
307
308     gst_caps_replace(&postproc->allowed_sinkpad_caps, NULL);
309     gst_caps_replace(&postproc->allowed_srcpad_caps, NULL);
310     gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
311 }
312
313 static gboolean
314 gst_vaapipostproc_start(GstBaseTransform *trans)
315 {
316     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
317
318     ds_reset(&postproc->deinterlace_state);
319     if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
320         return FALSE;
321     if (!gst_vaapipostproc_ensure_display(postproc))
322         return FALSE;
323     return TRUE;
324 }
325
326 static gboolean
327 gst_vaapipostproc_stop(GstBaseTransform *trans)
328 {
329     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
330
331     ds_reset(&postproc->deinterlace_state);
332     gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
333     postproc->filter_pool_active = FALSE;
334     return TRUE;
335 }
336
337 static gboolean
338 should_deinterlace_buffer(GstVaapiPostproc *postproc, GstBuffer *buf)
339 {
340     if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
341         postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
342         return FALSE;
343
344     if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
345         return TRUE;
346
347     g_assert(postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
348
349     switch (GST_VIDEO_INFO_INTERLACE_MODE(&postproc->sinkpad_info)) {
350     case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
351         return TRUE;
352     case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
353         return FALSE;
354     case GST_VIDEO_INTERLACE_MODE_MIXED:
355 #if GST_CHECK_VERSION(1,0,0)
356         if (GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
357             return TRUE;
358 #else
359         if (!GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_PROGRESSIVE))
360             return TRUE;
361 #endif
362         break;
363     default:
364         GST_ERROR("unhandled \"interlace-mode\", disabling deinterlacing" );
365         break;
366     }
367     return FALSE;
368 }
369
370 static GstBuffer *
371 create_output_buffer(GstVaapiPostproc *postproc)
372 {
373     GstBuffer *outbuf;
374
375 #if GST_CHECK_VERSION(1,0,0)
376     GstBufferPool * const pool =
377         GST_VAAPI_PLUGIN_BASE(postproc)->srcpad_buffer_pool;
378     GstFlowReturn ret;
379
380     g_return_val_if_fail(pool != NULL, NULL);
381
382     if (!postproc->filter_pool_active) {
383         if (!gst_buffer_pool_set_active(pool, TRUE))
384             goto error_activate_pool;
385         postproc->filter_pool_active = TRUE;
386     }
387
388     outbuf = NULL;
389     ret = gst_buffer_pool_acquire_buffer(pool, &outbuf, NULL);
390     if (ret != GST_FLOW_OK || !outbuf)
391         goto error_create_buffer;
392 #else
393     /* Create a raw VA video buffer without GstVaapiVideoMeta attached
394        to it yet, as this will be done next in the transform() hook */
395     outbuf = gst_vaapi_video_buffer_new_empty();
396     if (!outbuf)
397         goto error_create_buffer;
398
399     gst_buffer_set_caps(outbuf, GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAPS(postproc));
400 #endif
401     return outbuf;
402
403     /* ERRORS */
404 #if GST_CHECK_VERSION(1,0,0)
405 error_activate_pool:
406     {
407         GST_ERROR("failed to activate output video buffer pool");
408         return NULL;
409     }
410 #endif
411 error_create_buffer:
412     {
413         GST_ERROR("failed to create output video buffer");
414         return NULL;
415     }
416 }
417
418 static gboolean
419 append_output_buffer_metadata(GstVaapiPostproc *postproc, GstBuffer *outbuf,
420     GstBuffer *inbuf, guint flags)
421 {
422     GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
423     GstVaapiSurfaceProxy *proxy;
424
425     gst_buffer_copy_into(outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
426
427     /* GstVideoCropMeta */
428 #if GST_CHECK_VERSION(1,0,0)
429     if (!postproc->use_vpp) {
430         GstVideoCropMeta * const crop_meta =
431             gst_buffer_get_video_crop_meta(inbuf);
432         if (crop_meta) {
433             GstVideoCropMeta * const out_crop_meta =
434                 gst_buffer_add_video_crop_meta(outbuf);
435             if (out_crop_meta)
436                 *out_crop_meta = *crop_meta;
437         }
438     }
439 #endif
440
441     /* GstVaapiVideoMeta */
442     inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
443     g_return_val_if_fail(inbuf_meta != NULL, FALSE);
444     proxy = gst_vaapi_video_meta_get_surface_proxy(inbuf_meta);
445
446     outbuf_meta = gst_buffer_get_vaapi_video_meta(outbuf);
447     g_return_val_if_fail(outbuf_meta != NULL, FALSE);
448     proxy = gst_vaapi_surface_proxy_copy(proxy);
449     if (!proxy)
450         return FALSE;
451
452     gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
453     gst_vaapi_surface_proxy_unref(proxy);
454     return TRUE;
455 }
456
457 static gboolean
458 deint_method_is_advanced(GstVaapiDeinterlaceMethod deint_method)
459 {
460     gboolean is_advanced;
461
462     switch (deint_method) {
463     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
464     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
465         is_advanced = TRUE;
466         break;
467     default:
468         is_advanced = FALSE;
469         break;
470     }
471     return is_advanced;
472 }
473
474 static GstVaapiDeinterlaceMethod
475 get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
476 {
477     switch (deint_method) {
478     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
479         deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
480         break;
481     default:
482         /* Default to basic "bob" for all others */
483         deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
484         break;
485     }
486     return deint_method;
487 }
488
489 static gboolean
490 set_best_deint_method(GstVaapiPostproc *postproc, guint flags,
491     GstVaapiDeinterlaceMethod *deint_method_ptr)
492 {
493     GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
494     gboolean success;
495
496     for (;;) {
497         success = gst_vaapi_filter_set_deinterlacing(postproc->filter,
498             deint_method, flags);
499         if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
500             break;
501         deint_method = get_next_deint_method(deint_method);
502     }
503     *deint_method_ptr = deint_method;
504     return success;
505 }
506
507 static GstFlowReturn
508 gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
509     GstBuffer *outbuf)
510 {
511     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
512     GstVaapiDeinterlaceState * const ds = &postproc->deinterlace_state;
513     GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
514     GstVaapiSurface *inbuf_surface, *outbuf_surface;
515     GstVaapiSurfaceProxy *proxy;
516     GstVaapiFilterStatus status;
517     GstClockTime timestamp;
518     GstFlowReturn ret;
519     GstBuffer *fieldbuf;
520     GstVaapiDeinterlaceMethod deint_method;
521     guint flags, deint_flags;
522     gboolean tff, deint, deint_refs, deint_changed;
523     GstVaapiRectangle *crop_rect = NULL;
524 #if GST_CHECK_VERSION(1,0,0)
525     GstVaapiRectangle tmp_rect;
526 #endif
527
528     /* Validate filters */
529     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
530         !gst_vaapi_filter_set_format(postproc->filter, postproc->format))
531         return GST_FLOW_NOT_SUPPORTED;
532
533     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) &&
534         !gst_vaapi_filter_set_denoising_level(postproc->filter,
535             postproc->denoise_level))
536         return GST_FLOW_NOT_SUPPORTED;
537
538     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) &&
539         !gst_vaapi_filter_set_sharpening_level(postproc->filter,
540             postproc->sharpen_level))
541         return GST_FLOW_NOT_SUPPORTED;
542
543     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) &&
544         !gst_vaapi_filter_set_hue(postproc->filter,
545             postproc->hue))
546         return GST_FLOW_NOT_SUPPORTED;
547
548     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) &&
549         !gst_vaapi_filter_set_saturation(postproc->filter,
550             postproc->saturation))
551         return GST_FLOW_NOT_SUPPORTED;
552
553     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) &&
554         !gst_vaapi_filter_set_brightness(postproc->filter,
555             postproc->brightness))
556         return GST_FLOW_NOT_SUPPORTED;
557
558     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) &&
559         !gst_vaapi_filter_set_contrast(postproc->filter,
560             postproc->contrast))
561         return GST_FLOW_NOT_SUPPORTED;
562
563     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SCALE) &&
564         !gst_vaapi_filter_set_scaling(postproc->filter,
565             postproc->scale_method))
566         return GST_FLOW_NOT_SUPPORTED;
567
568     inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
569     if (!inbuf_meta)
570         goto error_invalid_buffer;
571     inbuf_surface = gst_vaapi_video_meta_get_surface(inbuf_meta);
572
573 #if GST_CHECK_VERSION(1,0,0)
574     GstVideoCropMeta * const crop_meta =
575         gst_buffer_get_video_crop_meta(inbuf);
576     if (crop_meta) {
577         crop_rect = &tmp_rect;
578         crop_rect->x = crop_meta->x;
579         crop_rect->y = crop_meta->y;
580         crop_rect->width = crop_meta->width;
581         crop_rect->height = crop_meta->height;
582     }
583 #endif
584     if (!crop_rect)
585         crop_rect = (GstVaapiRectangle *)
586             gst_vaapi_video_meta_get_render_rect(inbuf_meta);
587
588     timestamp  = GST_BUFFER_TIMESTAMP(inbuf);
589     tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
590     deint      = should_deinterlace_buffer(postproc, inbuf);
591
592     /* Drop references if deinterlacing conditions changed */
593     deint_changed = deint != ds->deint;
594     if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
595         ds_reset(ds);
596
597     deint_method = postproc->deinterlace_method;
598     deint_refs = deint_method_is_advanced(deint_method);
599     if (deint_refs && 0) {
600         GstBuffer * const prev_buf = ds_get_buffer(ds, 0);
601         GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP(inbuf);
602         /* Reset deinterlacing state when there is a discontinuity */
603         if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP(prev_buf)) != pts) {
604             const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF(prev_pts, pts);
605             if (pts_diff < 0 || (postproc->field_duration > 0 &&
606                     pts_diff >= postproc->field_duration * 3 - 1))
607                 ds_reset(ds);
608         }
609     }
610
611     ds->deint = deint;
612     ds->tff = tff;
613
614     flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
615         ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
616
617     /* First field */
618     if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
619         fieldbuf = create_output_buffer(postproc);
620         if (!fieldbuf)
621             goto error_create_buffer;
622
623         outbuf_meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
624         if (!outbuf_meta)
625             goto error_create_meta;
626
627         proxy = gst_vaapi_surface_proxy_new_from_pool(
628             GST_VAAPI_SURFACE_POOL(postproc->filter_pool));
629         if (!proxy)
630             goto error_create_proxy;
631         gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
632         gst_vaapi_surface_proxy_unref(proxy);
633
634         if (deint) {
635             deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
636             if (tff)
637                 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
638             if (!set_best_deint_method(postproc, deint_flags, &deint_method))
639                 goto error_op_deinterlace;
640
641             if (deint_method != postproc->deinterlace_method) {
642                 GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
643                           postproc->deinterlace_method, deint_method);
644                 postproc->deinterlace_method = deint_method;
645                 deint_refs = deint_method_is_advanced(deint_method);
646             }
647
648             if (deint_refs) {
649                 ds_set_surfaces(ds);
650                 if (!gst_vaapi_filter_set_deinterlacing_references(
651                         postproc->filter, ds->surfaces, ds->num_surfaces,
652                         NULL, 0))
653                     goto error_op_deinterlace;
654             }
655         }
656         else if (deint_changed) {
657             // Reset internal filter to non-deinterlacing mode
658             deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
659             if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
660                     deint_method, 0))
661                 goto error_op_deinterlace;
662         }
663
664         outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
665         gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
666         status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
667             outbuf_surface, flags);
668         if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
669             goto error_process_vpp;
670
671         GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
672         GST_BUFFER_DURATION(fieldbuf)  = postproc->field_duration;
673         ret = gst_pad_push(trans->srcpad, fieldbuf);
674         if (ret != GST_FLOW_OK)
675             goto error_push_buffer;
676     }
677     fieldbuf = NULL;
678
679     /* Second field */
680     outbuf_meta = gst_buffer_get_vaapi_video_meta(outbuf);
681     if (!outbuf_meta)
682         goto error_create_meta;
683
684     proxy = gst_vaapi_surface_proxy_new_from_pool(
685         GST_VAAPI_SURFACE_POOL(postproc->filter_pool));
686     if (!proxy)
687         goto error_create_proxy;
688     gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
689     gst_vaapi_surface_proxy_unref(proxy);
690
691     if (deint) {
692         deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
693         if (tff)
694             deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
695         if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
696                 deint_method, deint_flags))
697             goto error_op_deinterlace;
698
699         if (deint_refs && !gst_vaapi_filter_set_deinterlacing_references(
700                 postproc->filter, ds->surfaces, ds->num_surfaces, NULL, 0))
701             goto error_op_deinterlace;
702     }
703     else if (deint_changed && !gst_vaapi_filter_set_deinterlacing(
704                  postproc->filter, deint_method, 0))
705         goto error_op_deinterlace;
706
707     outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
708     gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
709     status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
710         outbuf_surface, flags);
711     if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
712         goto error_process_vpp;
713
714     if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
715         gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
716     else {
717         GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
718         GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
719     }
720
721     if (deint && deint_refs)
722         ds_add_buffer(ds, inbuf);
723     postproc->use_vpp = TRUE;
724     return GST_FLOW_OK;
725
726     /* ERRORS */
727 error_invalid_buffer:
728     {
729         GST_ERROR("failed to validate source buffer");
730         return GST_FLOW_ERROR;
731     }
732 error_create_buffer:
733     {
734         GST_ERROR("failed to create output buffer");
735         return GST_FLOW_ERROR;
736     }
737 error_create_meta:
738     {
739         GST_ERROR("failed to create new output buffer meta");
740         gst_buffer_replace(&fieldbuf, NULL);
741         return GST_FLOW_ERROR;
742     }
743 error_create_proxy:
744     {
745         GST_ERROR("failed to create surface proxy from pool");
746         gst_buffer_replace(&fieldbuf, NULL);
747         return GST_FLOW_ERROR;
748     }
749 error_op_deinterlace:
750     {
751         GST_ERROR("failed to apply deinterlacing filter");
752         gst_buffer_replace(&fieldbuf, NULL);
753         return GST_FLOW_NOT_SUPPORTED;
754     }
755 error_process_vpp:
756     {
757         GST_ERROR("failed to apply VPP filters (error %d)", status);
758         gst_buffer_replace(&fieldbuf, NULL);
759         return GST_FLOW_ERROR;
760     }
761 error_push_buffer:
762     {
763         if (ret != GST_FLOW_FLUSHING)
764             GST_ERROR("failed to push output buffer to video sink");
765         return GST_FLOW_ERROR;
766     }
767 }
768
769 static GstFlowReturn
770 gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
771     GstBuffer *outbuf)
772 {
773     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
774     GstVaapiVideoMeta *meta;
775     GstClockTime timestamp;
776     GstFlowReturn ret;
777     GstBuffer *fieldbuf;
778     guint fieldbuf_flags, outbuf_flags, flags;
779     gboolean tff, deint;
780
781     meta = gst_buffer_get_vaapi_video_meta(inbuf);
782     if (!meta)
783         goto error_invalid_buffer;
784
785     timestamp  = GST_BUFFER_TIMESTAMP(inbuf);
786     tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
787     deint      = should_deinterlace_buffer(postproc, inbuf);
788
789     flags = gst_vaapi_video_meta_get_render_flags(meta) &
790         ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
791
792     /* First field */
793     fieldbuf = create_output_buffer(postproc);
794     if (!fieldbuf)
795         goto error_create_buffer;
796     append_output_buffer_metadata(postproc, fieldbuf, inbuf, 0);
797
798     meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
799     fieldbuf_flags = flags;
800     fieldbuf_flags |= deint ? (
801         tff ?
802         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
803         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
804         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
805     gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
806
807     GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
808     GST_BUFFER_DURATION(fieldbuf)  = postproc->field_duration;
809     ret = gst_pad_push(trans->srcpad, fieldbuf);
810     if (ret != GST_FLOW_OK)
811         goto error_push_buffer;
812
813     /* Second field */
814     append_output_buffer_metadata(postproc, outbuf, inbuf, 0);
815
816     meta = gst_buffer_get_vaapi_video_meta(outbuf);
817     outbuf_flags = flags;
818     outbuf_flags |= deint ? (
819         tff ?
820         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
821         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
822         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
823     gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
824
825     GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
826     GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
827     return GST_FLOW_OK;
828
829     /* ERRORS */
830 error_invalid_buffer:
831     {
832         GST_ERROR("failed to validate source buffer");
833         return GST_FLOW_ERROR;
834     }
835 error_create_buffer:
836     {
837         GST_ERROR("failed to create output buffer");
838         return GST_FLOW_EOS;
839     }
840 error_push_buffer:
841     {
842         if (ret != GST_FLOW_FLUSHING)
843             GST_ERROR("failed to push output buffer to video sink");
844         return GST_FLOW_EOS;
845     }
846 }
847
848 static GstFlowReturn
849 gst_vaapipostproc_passthrough(GstBaseTransform *trans, GstBuffer *inbuf,
850     GstBuffer *outbuf)
851 {
852     GstVaapiVideoMeta *meta;
853
854     /* No video processing needed, simply copy buffer metadata */
855     meta = gst_buffer_get_vaapi_video_meta(inbuf);
856     if (!meta)
857         goto error_invalid_buffer;
858
859     append_output_buffer_metadata(GST_VAAPIPOSTPROC(trans), outbuf, inbuf,
860         GST_BUFFER_COPY_TIMESTAMPS);
861     return GST_FLOW_OK;
862
863     /* ERRORS */
864 error_invalid_buffer:
865     {
866         GST_ERROR("failed to validate source buffer");
867         return GST_FLOW_ERROR;
868     }
869 }
870
871 static gboolean
872 is_deinterlace_enabled(GstVaapiPostproc *postproc, GstVideoInfo *vip)
873 {
874     gboolean deinterlace;
875
876     switch (postproc->deinterlace_mode) {
877     case GST_VAAPI_DEINTERLACE_MODE_AUTO:
878         deinterlace = GST_VIDEO_INFO_IS_INTERLACED(vip);
879         break;
880     case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
881         deinterlace = TRUE;
882         break;
883     default:
884         deinterlace = FALSE;
885         break;
886     }
887     return deinterlace;
888 }
889
890 static gboolean
891 video_info_changed(GstVideoInfo *old_vip, GstVideoInfo *new_vip)
892 {
893     if (GST_VIDEO_INFO_FORMAT(old_vip) != GST_VIDEO_INFO_FORMAT(new_vip))
894         return TRUE;
895     if (GST_VIDEO_INFO_INTERLACE_MODE(old_vip) !=
896         GST_VIDEO_INFO_INTERLACE_MODE(new_vip))
897         return TRUE;
898     if (GST_VIDEO_INFO_WIDTH(old_vip) != GST_VIDEO_INFO_WIDTH(new_vip))
899         return TRUE;
900     if (GST_VIDEO_INFO_HEIGHT(old_vip) != GST_VIDEO_INFO_HEIGHT(new_vip))
901         return TRUE;
902     return FALSE;
903 }
904
905 static gboolean
906 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
907     gboolean *caps_changed_ptr)
908 {
909     GstVideoInfo vi;
910     gboolean deinterlace;
911
912     if (!gst_video_info_from_caps(&vi, caps))
913         return FALSE;
914
915     if (video_info_changed(&vi, &postproc->sinkpad_info))
916         postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
917
918     deinterlace = is_deinterlace_enabled(postproc, &vi);
919     if (deinterlace)
920         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
921     postproc->field_duration = GST_VIDEO_INFO_FPS_N(&vi) > 0 ?
922         gst_util_uint64_scale(GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
923             (1 + deinterlace) * GST_VIDEO_INFO_FPS_N(&vi)) : 0;
924
925     postproc->get_va_surfaces = gst_caps_has_vaapi_surface(caps);
926     return TRUE;
927 }
928
929 static gboolean
930 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps,
931     gboolean *caps_changed_ptr)
932 {
933     GstVideoInfo vi;
934
935     if (!gst_video_info_from_caps(&vi, caps))
936         return FALSE;
937
938     if (video_info_changed(&vi, &postproc->srcpad_info))
939         postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
940
941     if (postproc->format != GST_VIDEO_INFO_FORMAT(&postproc->sinkpad_info) &&
942         postproc->format != DEFAULT_FORMAT)
943         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
944
945     if ((postproc->width || postproc->height) &&
946         postproc->width != GST_VIDEO_INFO_WIDTH(&postproc->sinkpad_info) &&
947         postproc->height != GST_VIDEO_INFO_HEIGHT(&postproc->sinkpad_info))
948         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
949     return TRUE;
950 }
951
952 static gboolean
953 ensure_allowed_sinkpad_caps(GstVaapiPostproc *postproc)
954 {
955     GstCaps *out_caps, *raw_caps;
956
957     if (postproc->allowed_sinkpad_caps)
958         return TRUE;
959
960     /* Create VA caps */
961     out_caps = gst_caps_from_string(GST_VAAPIPOSTPROC_SURFACE_CAPS ", "
962         GST_CAPS_INTERLACED_MODES);
963     if (!out_caps) {
964         GST_ERROR("failed to create VA sink caps");
965         return FALSE;
966     }
967
968     /* Append raw video caps */
969     if (gst_vaapipostproc_ensure_uploader(postproc)) {
970         raw_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(postproc);
971         if (raw_caps) {
972             out_caps = gst_caps_make_writable(out_caps);
973             gst_caps_append(out_caps, gst_caps_copy(raw_caps));
974         }
975         else
976             GST_WARNING("failed to create YUV sink caps");
977     }
978     postproc->allowed_sinkpad_caps = out_caps;
979
980     /* XXX: append VA/VPP filters */
981     return TRUE;
982 }
983
984 /* Fixup output caps so that to reflect the supported set of pixel formats */
985 static GstCaps *
986 expand_allowed_srcpad_caps(GstVaapiPostproc *postproc, GstCaps *caps)
987 {
988     GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
989     guint i, num_structures;
990     gboolean had_filter;
991
992     had_filter = postproc->filter != NULL;
993     if (!had_filter && !gst_vaapipostproc_ensure_filter(postproc))
994         goto cleanup;
995     if (!gst_vaapipostproc_ensure_filter_caps(postproc))
996         goto cleanup;
997
998     /* Reset "format" field for each structure */
999     if (!gst_vaapi_value_set_format_list(&value, postproc->filter_formats))
1000         goto cleanup;
1001     if (gst_vaapi_value_set_format(&v_format, GST_VIDEO_FORMAT_ENCODED)) {
1002         gst_value_list_prepend_value(&value, &v_format);
1003         g_value_unset(&v_format);
1004     }
1005
1006     num_structures = gst_caps_get_size(caps);
1007     for (i = 0; i < num_structures; i++) {
1008 #if GST_CHECK_VERSION(1,1,0)
1009         GstCapsFeatures * const features = gst_caps_get_features (caps, i);
1010         if (gst_caps_features_contains(features,
1011                 GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META))
1012             continue;
1013 #endif
1014
1015         GstStructure * const structure = gst_caps_get_structure(caps, i);
1016         if (!structure)
1017             continue;
1018         gst_structure_set_value(structure, "format", &value);
1019     }
1020     g_value_unset(&value);
1021
1022 cleanup:
1023     if (!had_filter)
1024         gst_vaapipostproc_destroy_filter(postproc);
1025     return caps;
1026 }
1027
1028 static gboolean
1029 ensure_allowed_srcpad_caps(GstVaapiPostproc *postproc)
1030 {
1031     GstCaps *out_caps;
1032
1033     if (postproc->allowed_srcpad_caps)
1034         return TRUE;
1035
1036     /* Create initial caps from pad template */
1037     out_caps = gst_caps_from_string(gst_vaapipostproc_src_caps_str);
1038     if (!out_caps) {
1039         GST_ERROR("failed to create VA src caps");
1040         return FALSE;
1041     }
1042
1043     postproc->allowed_srcpad_caps =
1044         expand_allowed_srcpad_caps(postproc, out_caps);
1045     return postproc->allowed_srcpad_caps != NULL;
1046 }
1047
1048 static void
1049 find_best_size(GstVaapiPostproc *postproc, GstVideoInfo *vip,
1050     guint *width_ptr, guint *height_ptr)
1051 {
1052     guint width, height;
1053
1054     width  = GST_VIDEO_INFO_WIDTH(vip);
1055     height = GST_VIDEO_INFO_HEIGHT(vip);
1056     if (postproc->width && postproc->height) {
1057         width = postproc->width;
1058         height = postproc->height;
1059     }
1060     else if (postproc->keep_aspect) {
1061         const gdouble ratio  = (gdouble)width / height;
1062         if (postproc->width) {
1063             width = postproc->width;
1064             height = postproc->width / ratio;
1065         }
1066         else if (postproc->height) {
1067             height = postproc->height;
1068             width = postproc->height * ratio;
1069         }
1070     }
1071     else if (postproc->width)
1072         width = postproc->width;
1073     else if (postproc->height)
1074         height = postproc->height;
1075
1076     *width_ptr = width;
1077     *height_ptr = height;
1078 }
1079
1080 static GstCaps *
1081 gst_vaapipostproc_transform_caps_impl(GstBaseTransform *trans,
1082     GstPadDirection direction, GstCaps *caps)
1083 {
1084     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1085     GstVideoInfo vi;
1086     GstVideoFormat format, out_format;
1087     GstCaps *out_caps;
1088 #if GST_CHECK_VERSION(1,1,0)
1089     GstVaapiCapsFeature feature;
1090     const gchar *feature_str;
1091 #endif
1092     guint width, height;
1093
1094     /* Generate the sink pad caps, that could be fixated afterwards */
1095     if (direction == GST_PAD_SRC) {
1096         if (!ensure_allowed_sinkpad_caps(postproc))
1097             return NULL;
1098         return gst_caps_ref(postproc->allowed_sinkpad_caps);
1099     }
1100
1101     /* Generate complete set of src pad caps if non-fixated sink pad
1102        caps are provided */
1103     if (!gst_caps_is_fixed(caps)) {
1104         if (!ensure_allowed_srcpad_caps(postproc))
1105             return NULL;
1106         return gst_caps_ref(postproc->allowed_srcpad_caps);
1107     }
1108
1109     /* Generate the expected src pad caps, from the current fixated
1110        sink pad caps */
1111     if (!gst_video_info_from_caps(&vi, caps))
1112         return NULL;
1113
1114     // Set double framerate in interlaced mode
1115     if (is_deinterlace_enabled(postproc, &vi)) {
1116         gint fps_n = GST_VIDEO_INFO_FPS_N(&vi);
1117         gint fps_d = GST_VIDEO_INFO_FPS_D(&vi);
1118         if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
1119             return NULL;
1120         GST_VIDEO_INFO_FPS_N(&vi) = fps_n;
1121         GST_VIDEO_INFO_FPS_D(&vi) = fps_d;
1122     }
1123
1124     // Signal the other pad that we only generate progressive frames
1125     GST_VIDEO_INFO_INTERLACE_MODE(&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
1126
1127     // Update size from user-specified parameters
1128     format = GST_VIDEO_INFO_FORMAT(&vi);
1129 #if GST_CHECK_VERSION(1,1,0)
1130     out_format = postproc->format;
1131 #else
1132     out_format = GST_VIDEO_FORMAT_ENCODED;
1133 #endif
1134     find_best_size(postproc, &vi, &width, &height);
1135     gst_video_info_set_format(&vi, out_format, width, height);
1136
1137 #if GST_CHECK_VERSION(1,1,0)
1138     out_caps = gst_video_info_to_caps(&vi);
1139     if (!out_caps)
1140         return NULL;
1141
1142     feature = gst_vaapi_find_preferred_caps_feature(
1143         GST_BASE_TRANSFORM_SRC_PAD(trans), out_format);
1144     if (feature) {
1145         feature_str = gst_vaapi_caps_feature_to_string(feature);
1146         if (feature_str)
1147             gst_caps_set_features(out_caps, 0,
1148                 gst_caps_features_new(feature_str, NULL));
1149
1150         if (out_format == GST_VIDEO_FORMAT_ENCODED &&
1151             feature != GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE) {
1152             GstCaps *sink_caps, *peer_caps =
1153                 gst_pad_peer_query_caps(GST_BASE_TRANSFORM_SRC_PAD(trans),
1154                     postproc->allowed_srcpad_caps);
1155
1156             if (feature == GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META)
1157                 format = GST_VIDEO_FORMAT_RGBA;
1158
1159             gst_video_info_set_format(&vi, format, width, height);
1160             sink_caps = gst_video_info_to_caps(&vi);
1161             if (sink_caps) {
1162                 if (feature_str)
1163                     gst_caps_set_features(sink_caps, 0,
1164                         gst_caps_features_new(feature_str, NULL));
1165                 if (gst_caps_can_intersect(peer_caps, sink_caps))
1166                     gst_caps_set_simple(out_caps, "format", G_TYPE_STRING,
1167                         gst_video_format_to_string(format), NULL);
1168                 gst_caps_unref(sink_caps);
1169             }
1170             gst_caps_unref(peer_caps);
1171         }
1172     }
1173 #else
1174     /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
1175        reconstruct suitable caps for "encoded" video formats */
1176     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
1177     if (!out_caps)
1178         return NULL;
1179
1180     gst_caps_set_simple(out_caps,
1181         "type", G_TYPE_STRING, "vaapi",
1182         "opengl", G_TYPE_BOOLEAN, USE_GLX,
1183         "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH(&vi),
1184         "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT(&vi),
1185         "framerate", GST_TYPE_FRACTION, GST_VIDEO_INFO_FPS_N(&vi),
1186             GST_VIDEO_INFO_FPS_D(&vi),
1187         "pixel-aspect-ratio", GST_TYPE_FRACTION, GST_VIDEO_INFO_PAR_N(&vi),
1188             GST_VIDEO_INFO_PAR_D(&vi),
1189         NULL);
1190
1191     gst_caps_set_interlaced(out_caps, &vi);
1192 #endif
1193     return out_caps;
1194 }
1195
1196 #if GST_CHECK_VERSION(1,0,0)
1197 static GstCaps *
1198 gst_vaapipostproc_transform_caps(GstBaseTransform *trans,
1199     GstPadDirection direction, GstCaps *caps, GstCaps *filter)
1200 {
1201     GstCaps *out_caps;
1202
1203     caps = gst_vaapipostproc_transform_caps_impl(trans, direction, caps);
1204     if (caps && filter) {
1205         out_caps = gst_caps_intersect_full(caps, filter,
1206             GST_CAPS_INTERSECT_FIRST);
1207         gst_caps_unref(caps);
1208         return out_caps;
1209     }
1210     return caps;
1211 }
1212 #else
1213 #define gst_vaapipostproc_transform_caps \
1214     gst_vaapipostproc_transform_caps_impl
1215 #endif
1216
1217 #if GST_CHECK_VERSION(1,0,0)
1218 typedef gsize GstBaseTransformSizeType;
1219 #else
1220 typedef guint GstBaseTransformSizeType;
1221 #endif
1222
1223 static gboolean
1224 gst_vaapipostproc_transform_size(GstBaseTransform *trans,
1225     GstPadDirection direction, GstCaps *caps, GstBaseTransformSizeType size,
1226     GstCaps *othercaps, GstBaseTransformSizeType *othersize)
1227 {
1228     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1229
1230     if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1231         *othersize = 0;
1232     else
1233         *othersize = size;
1234     return TRUE;
1235 }
1236
1237 static GstFlowReturn
1238 gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
1239     GstBuffer *outbuf)
1240 {
1241     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1242     GstBuffer *buf;
1243     GstFlowReturn ret;
1244
1245     ret = gst_vaapi_plugin_base_get_input_buffer(
1246         GST_VAAPI_PLUGIN_BASE(postproc), inbuf, &buf);
1247     if (ret != GST_FLOW_OK)
1248         return GST_FLOW_ERROR;
1249
1250     ret = GST_FLOW_NOT_SUPPORTED;
1251     if (postproc->flags) {
1252         /* Use VA/VPP extensions to process this frame */
1253         if (postproc->has_vpp &&
1254             (postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE ||
1255              deint_method_is_advanced(postproc->deinterlace_method))) {
1256             ret = gst_vaapipostproc_process_vpp(trans, buf, outbuf);
1257             if (ret != GST_FLOW_NOT_SUPPORTED)
1258                 goto done;
1259             GST_WARNING("unsupported VPP filters. Disabling");
1260         }
1261
1262         /* Only append picture structure meta data (top/bottom field) */
1263         if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1264             ret = gst_vaapipostproc_process(trans, buf, outbuf);
1265             if (ret != GST_FLOW_NOT_SUPPORTED)
1266                 goto done;
1267         }
1268     }
1269
1270     /* Fallback: passthrough to the downstream element as is */
1271     ret = gst_vaapipostproc_passthrough(trans, buf, outbuf);
1272
1273 done:
1274     gst_buffer_unref(buf);
1275     return ret;
1276 }
1277
1278 static GstFlowReturn
1279 gst_vaapipostproc_prepare_output_buffer(GstBaseTransform *trans,
1280     GstBuffer *inbuf,
1281 #if !GST_CHECK_VERSION(1,0,0)
1282     gint size, GstCaps *caps,
1283 #endif
1284     GstBuffer **outbuf_ptr)
1285 {
1286     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1287
1288     *outbuf_ptr = create_output_buffer(postproc);
1289     return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
1290 }
1291
1292 static gboolean
1293 ensure_srcpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
1294 {
1295     GstVideoInfo vi;
1296     GstVaapiVideoPool *pool;
1297
1298     gst_video_info_init(&vi);
1299     gst_video_info_from_caps(&vi, caps);
1300     gst_video_info_set_format(&vi, postproc->format,
1301         GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
1302
1303     if (postproc->filter_pool && !video_info_changed(&vi, &postproc->filter_pool_info))
1304         return TRUE;
1305     postproc->filter_pool_info = vi;
1306
1307     pool = gst_vaapi_surface_pool_new(GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc),
1308         &postproc->filter_pool_info);
1309     if (!pool)
1310         return FALSE;
1311
1312     gst_vaapi_video_pool_replace(&postproc->filter_pool, pool);
1313     gst_vaapi_video_pool_unref(pool);
1314     return TRUE;
1315 }
1316
1317 static gboolean
1318 gst_vaapipostproc_set_caps(GstBaseTransform *trans, GstCaps *caps,
1319     GstCaps *out_caps)
1320 {
1321     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1322     gboolean caps_changed = FALSE;
1323
1324     if (!gst_vaapipostproc_update_sink_caps(postproc, caps, &caps_changed))
1325         return FALSE;
1326     if (!gst_vaapipostproc_update_src_caps(postproc, out_caps, &caps_changed))
1327         return FALSE;
1328
1329     if (caps_changed) {
1330         gst_vaapipostproc_destroy(postproc);
1331         if (!gst_vaapipostproc_create(postproc))
1332             return FALSE;
1333         if (!gst_vaapi_plugin_base_set_caps(GST_VAAPI_PLUGIN_BASE(trans),
1334                 caps, out_caps))
1335             return FALSE;
1336     }
1337
1338     if (!ensure_srcpad_buffer_pool(postproc, out_caps))
1339         return FALSE;
1340     return TRUE;
1341 }
1342
1343 static gboolean
1344 gst_vaapipostproc_query(GstBaseTransform *trans, GstPadDirection direction,
1345     GstQuery *query)
1346 {
1347     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1348
1349     GST_INFO_OBJECT(trans, "query type `%s'", GST_QUERY_TYPE_NAME(query));
1350
1351     if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc))) {
1352         GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
1353         return TRUE;
1354     }
1355
1356     return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
1357         trans, direction, query);
1358 }
1359
1360 #if GST_CHECK_VERSION(1,0,0)
1361 static gboolean
1362 gst_vaapipostproc_propose_allocation(GstBaseTransform *trans,
1363     GstQuery *decide_query, GstQuery *query)
1364 {
1365     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1366     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(trans);
1367
1368     /* Let vaapidecode allocate the video buffers */
1369     if (postproc->get_va_surfaces)
1370         return FALSE;
1371     if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1372         return FALSE;
1373     return TRUE;
1374 }
1375
1376 static gboolean
1377 gst_vaapipostproc_decide_allocation(GstBaseTransform *trans, GstQuery *query)
1378 {
1379     return gst_vaapi_plugin_base_decide_allocation(GST_VAAPI_PLUGIN_BASE(trans),
1380         query, 0);
1381 }
1382 #endif
1383
1384 static void
1385 gst_vaapipostproc_finalize(GObject *object)
1386 {
1387     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1388
1389     gst_vaapipostproc_destroy(postproc);
1390
1391     gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(postproc));
1392     G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
1393 }
1394
1395 static void
1396 gst_vaapipostproc_set_property(
1397     GObject      *object,
1398     guint         prop_id,
1399     const GValue *value,
1400     GParamSpec   *pspec
1401 )
1402 {
1403     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1404
1405     switch (prop_id) {
1406     case PROP_FORMAT:
1407         postproc->format = g_value_get_enum(value);
1408         break;
1409     case PROP_WIDTH:
1410         postproc->width = g_value_get_uint(value);
1411         break;
1412     case PROP_HEIGHT:
1413         postproc->height = g_value_get_uint(value);
1414         break;
1415     case PROP_FORCE_ASPECT_RATIO:
1416         postproc->keep_aspect = g_value_get_boolean(value);
1417         break;
1418     case PROP_DEINTERLACE_MODE:
1419         postproc->deinterlace_mode = g_value_get_enum(value);
1420         break;
1421     case PROP_DEINTERLACE_METHOD:
1422         postproc->deinterlace_method = g_value_get_enum(value);
1423         break;
1424      case PROP_DENOISE:
1425          postproc->denoise_level = g_value_get_float(value);
1426          postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1427          break;
1428      case PROP_SHARPEN:
1429          postproc->sharpen_level = g_value_get_float(value);
1430          postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1431          break;
1432     case PROP_HUE:
1433         postproc->hue = g_value_get_float(value);
1434         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1435         break;
1436     case PROP_SATURATION:
1437         postproc->saturation = g_value_get_float(value);
1438         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1439         break;
1440     case PROP_BRIGHTNESS:
1441         postproc->brightness = g_value_get_float(value);
1442         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1443         break;
1444     case PROP_CONTRAST:
1445         postproc->contrast = g_value_get_float(value);
1446         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1447         break;
1448     case PROP_SCALE_METHOD:
1449         postproc->scale_method = g_value_get_enum(value);
1450         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SCALE;
1451         break;
1452     default:
1453         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1454         break;
1455     }
1456 }
1457
1458 static void
1459 gst_vaapipostproc_get_property(
1460     GObject    *object,
1461     guint       prop_id,
1462     GValue     *value,
1463     GParamSpec *pspec
1464 )
1465 {
1466     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1467
1468     switch (prop_id) {
1469     case PROP_FORMAT:
1470         g_value_set_enum(value, postproc->format);
1471         break;
1472     case PROP_WIDTH:
1473         g_value_set_uint(value, postproc->width);
1474         break;
1475     case PROP_HEIGHT:
1476         g_value_set_uint(value, postproc->height);
1477         break;
1478     case PROP_FORCE_ASPECT_RATIO:
1479         g_value_set_boolean(value, postproc->keep_aspect);
1480         break;
1481     case PROP_DEINTERLACE_MODE:
1482         g_value_set_enum(value, postproc->deinterlace_mode);
1483         break;
1484     case PROP_DEINTERLACE_METHOD:
1485         g_value_set_enum(value, postproc->deinterlace_method);
1486         break;
1487     case PROP_DENOISE:
1488         g_value_set_float(value, postproc->denoise_level);
1489         break;
1490     case PROP_SHARPEN:
1491         g_value_set_float(value, postproc->sharpen_level);
1492         break;
1493     case PROP_HUE:
1494         g_value_set_float(value, postproc->hue);
1495         break;
1496     case PROP_SATURATION:
1497         g_value_set_float(value, postproc->saturation);
1498         break;
1499     case PROP_BRIGHTNESS:
1500         g_value_set_float(value, postproc->brightness);
1501         break;
1502     case PROP_CONTRAST:
1503         g_value_set_float(value, postproc->contrast);
1504         break;
1505     case PROP_SCALE_METHOD:
1506         g_value_set_enum(value, postproc->scale_method);
1507         break;
1508     default:
1509         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1510         break;
1511     }
1512 }
1513
1514 static void
1515 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
1516 {
1517     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
1518     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
1519     GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
1520     GstPadTemplate *pad_template;
1521     GPtrArray *filter_ops;
1522     GstVaapiFilterOpInfo *filter_op;
1523
1524     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
1525                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1526
1527     gst_vaapi_plugin_base_class_init(GST_VAAPI_PLUGIN_BASE_CLASS(klass));
1528
1529     object_class->finalize      = gst_vaapipostproc_finalize;
1530     object_class->set_property  = gst_vaapipostproc_set_property;
1531     object_class->get_property  = gst_vaapipostproc_get_property;
1532     trans_class->start          = gst_vaapipostproc_start;
1533     trans_class->stop           = gst_vaapipostproc_stop;
1534     trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1535     trans_class->transform_size = gst_vaapipostproc_transform_size;
1536     trans_class->transform      = gst_vaapipostproc_transform;
1537     trans_class->set_caps       = gst_vaapipostproc_set_caps;
1538     trans_class->query          = gst_vaapipostproc_query;
1539
1540 #if GST_CHECK_VERSION(1,0,0)
1541     trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1542     trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
1543 #endif
1544
1545     trans_class->prepare_output_buffer =
1546         gst_vaapipostproc_prepare_output_buffer;
1547
1548     gst_element_class_set_static_metadata(element_class,
1549         "VA-API video postprocessing",
1550         "Filter/Converter/Video",
1551         GST_PLUGIN_DESC,
1552         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1553
1554     /* sink pad */
1555     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
1556     gst_element_class_add_pad_template(element_class, pad_template);
1557
1558     /* src pad */
1559     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
1560     gst_element_class_add_pad_template(element_class, pad_template);
1561
1562     /**
1563      * GstVaapiPostproc:deinterlace-mode:
1564      *
1565      * This selects whether the deinterlacing should always be applied or if
1566      * they should only be applied on content that has the "interlaced" flag
1567      * on the caps.
1568      */
1569     g_object_class_install_property
1570         (object_class,
1571          PROP_DEINTERLACE_MODE,
1572          g_param_spec_enum("deinterlace-mode",
1573                            "Deinterlace mode",
1574                            "Deinterlace mode to use",
1575                            GST_VAAPI_TYPE_DEINTERLACE_MODE,
1576                            DEFAULT_DEINTERLACE_MODE,
1577                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1578
1579     /**
1580      * GstVaapiPostproc:deinterlace-method:
1581      *
1582      * This selects the deinterlacing method to apply.
1583      */
1584     g_object_class_install_property
1585         (object_class,
1586          PROP_DEINTERLACE_METHOD,
1587          g_param_spec_enum("deinterlace-method",
1588                            "Deinterlace method",
1589                            "Deinterlace method to use",
1590                            GST_VAAPI_TYPE_DEINTERLACE_METHOD,
1591                            DEFAULT_DEINTERLACE_METHOD,
1592                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1593
1594     filter_ops = gst_vaapi_filter_get_operations(NULL);
1595     if (!filter_ops)
1596         return;
1597
1598     /**
1599      * GstVaapiPostproc:format:
1600      *
1601      * The forced output pixel format, expressed as a #GstVideoFormat.
1602      */
1603     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
1604     if (filter_op)
1605         g_object_class_install_property(object_class,
1606             PROP_FORMAT, filter_op->pspec);
1607
1608     /**
1609      * GstVaapiPostproc:width:
1610      *
1611      * The forced output width in pixels. If set to zero, the width is
1612      * calculated from the height if aspect ration is preserved, or
1613      * inherited from the sink caps width
1614      */
1615     g_object_class_install_property
1616         (object_class,
1617          PROP_WIDTH,
1618          g_param_spec_uint("width",
1619                            "Width",
1620                            "Forced output width",
1621                            0, G_MAXINT, 0,
1622                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1623
1624     /**
1625      * GstVaapiPostproc:height:
1626      *
1627      * The forced output height in pixels. If set to zero, the height
1628      * is calculated from the width if aspect ration is preserved, or
1629      * inherited from the sink caps height
1630      */
1631     g_object_class_install_property
1632         (object_class,
1633          PROP_HEIGHT,
1634          g_param_spec_uint("height",
1635                            "Height",
1636                            "Forced output height",
1637                            0, G_MAXINT, 0,
1638                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1639
1640     /**
1641      * GstVaapiPostproc:force-aspect-ratio:
1642      *
1643      * When enabled, scaling respects video aspect ratio; when
1644      * disabled, the video is distorted to fit the width and height
1645      * properties.
1646      */
1647     g_object_class_install_property
1648         (object_class,
1649          PROP_FORCE_ASPECT_RATIO,
1650          g_param_spec_boolean("force-aspect-ratio",
1651                               "Force aspect ratio",
1652                               "When enabled, scaling will respect original aspect ratio",
1653                               TRUE,
1654                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1655
1656     /**
1657      * GstVaapiPostproc:denoise:
1658      *
1659      * The level of noise reduction to apply.
1660      */
1661     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
1662     if (filter_op)
1663         g_object_class_install_property(object_class,
1664             PROP_DENOISE, filter_op->pspec);
1665
1666     /**
1667      * GstVaapiPostproc:sharpen:
1668      *
1669      * The level of sharpening to apply for positive values, or the
1670      * level of blurring for negative values.
1671      */
1672     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
1673     if (filter_op)
1674         g_object_class_install_property(object_class,
1675             PROP_SHARPEN, filter_op->pspec);
1676
1677     /**
1678      * GstVaapiPostproc:hue:
1679      *
1680      * The color hue, expressed as a float value. Range is -180.0 to
1681      * 180.0. Default value is 0.0 and represents no modification.
1682      */
1683     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_HUE);
1684     if (filter_op)
1685         g_object_class_install_property(object_class,
1686             PROP_HUE, filter_op->pspec);
1687
1688     /**
1689      * GstVaapiPostproc:saturation:
1690      *
1691      * The color saturation, expressed as a float value. Range is 0.0
1692      * to 2.0. Default value is 1.0 and represents no modification.
1693      */
1694     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
1695     if (filter_op)
1696         g_object_class_install_property(object_class,
1697             PROP_SATURATION, filter_op->pspec);
1698
1699     /**
1700      * GstVaapiPostproc:brightness:
1701      *
1702      * The color brightness, expressed as a float value. Range is -1.0
1703      * to 1.0. Default value is 0.0 and represents no modification.
1704      */
1705     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
1706     if (filter_op)
1707         g_object_class_install_property(object_class,
1708             PROP_BRIGHTNESS, filter_op->pspec);
1709
1710     /**
1711      * GstVaapiPostproc:contrast:
1712      *
1713      * The color contrast, expressed as a float value. Range is 0.0 to
1714      * 2.0. Default value is 1.0 and represents no modification.
1715      */
1716     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
1717     if (filter_op)
1718         g_object_class_install_property(object_class,
1719             PROP_CONTRAST, filter_op->pspec);
1720
1721     /**
1722      * GstVaapiPostproc:scale-method:
1723      *
1724      * The scaling method to use, expressed as an enum value. See
1725      * #GstVaapiScaleMethod.
1726      */
1727     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SCALING);
1728     if (filter_op)
1729         g_object_class_install_property(object_class,
1730             PROP_SCALE_METHOD, filter_op->pspec);
1731
1732     g_ptr_array_unref(filter_ops);
1733 }
1734
1735 static void
1736 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
1737 {
1738     gst_vaapi_plugin_base_init(GST_VAAPI_PLUGIN_BASE(postproc), GST_CAT_DEFAULT);
1739
1740     postproc->format                    = DEFAULT_FORMAT;
1741     postproc->deinterlace_mode          = DEFAULT_DEINTERLACE_MODE;
1742     postproc->deinterlace_method        = DEFAULT_DEINTERLACE_METHOD;
1743     postproc->field_duration            = GST_CLOCK_TIME_NONE;
1744     postproc->keep_aspect               = TRUE;
1745     postproc->get_va_surfaces           = TRUE;
1746
1747     gst_video_info_init(&postproc->sinkpad_info);
1748     gst_video_info_init(&postproc->srcpad_info);
1749     gst_video_info_init(&postproc->filter_pool_info);
1750 }