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