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