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