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