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