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