vadeinterlace, vapostproc: Drop output buffer if process failed.
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / va / gstvadeinterlace.c
1 /* GStreamer
2  * Copyright (C) 2021 Igalia, S.L.
3  *     Author: Víctor Jáquez <vjaquez@igalia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the0
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-vadeinterlace
23  * @title: vadeinterlace
24  * @short_description: A VA-API base video deinterlace filter
25  *
26  * vadeinterlace deinterlaces interlaced video frames to progressive
27  * video frames. This element and its deinterlacing methods depend on
28  * the installed and chosen [VA-API](https://01.org/linuxmedia/vaapi)
29  * driver, but it's usually avaialble with bob (linear) method.
30  *
31  * This element doesn't change the caps features, it only negotiates
32  * the same dowstream and upstream.
33  *
34  * ## Example launch line
35  * ```
36  * gst-launch-1.0 filesrc location=interlaced_video.mp4 ! parsebin ! vah264dec ! vadeinterlace ! vapostproc ! autovideosink
37  * ```
38  *
39  * Since: 1.20
40  *
41  */
42
43 /* ToDo:
44  *
45  * + field property to select only one field and keep the same framerate
46  */
47
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51
52 #include "gstvadeinterlace.h"
53
54 #include <gst/va/gstva.h>
55 #include <gst/video/video.h>
56 #include <va/va_drmcommon.h>
57
58 #include "gstvabasetransform.h"
59 #include "gstvacaps.h"
60 #include "gstvadisplay_priv.h"
61 #include "gstvafilter.h"
62
63 GST_DEBUG_CATEGORY_STATIC (gst_va_deinterlace_debug);
64 #define GST_CAT_DEFAULT gst_va_deinterlace_debug
65
66 #define GST_VA_DEINTERLACE(obj)           ((GstVaDeinterlace *) obj)
67 #define GST_VA_DEINTERLACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaDeinterlaceClass))
68 #define GST_VA_DEINTERLACE_CLASS(klass)    ((GstVaDeinterlaceClass *) klass)
69
70 typedef struct _GstVaDeinterlace GstVaDeinterlace;
71 typedef struct _GstVaDeinterlaceClass GstVaDeinterlaceClass;
72
73 enum CurrField
74 {
75   UNKNOWN_FIELD,
76   FIRST_FIELD,
77   SECOND_FIELD,
78   FINISHED,
79 };
80
81 struct _GstVaDeinterlaceClass
82 {
83   /* GstVideoFilter overlaps functionality */
84   GstVaBaseTransformClass parent_class;
85 };
86
87 struct _GstVaDeinterlace
88 {
89   GstVaBaseTransform parent;
90
91   gboolean rebuild_filters;
92   VAProcDeinterlacingType method;
93
94   guint num_backward_references;
95
96   GstBuffer *history[8];
97   gint hcount;
98   gint hdepth;
99   gint hcurr;
100   enum CurrField curr_field;
101
102   /* Calculated buffer duration by using upstream framerate */
103   GstClockTime default_duration;
104 };
105
106 static GstElementClass *parent_class = NULL;
107
108 struct CData
109 {
110   gchar *render_device_path;
111   gchar *description;
112 };
113
114 /* *INDENT-OFF* */
115 static const gchar *caps_str =
116     GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VA,
117         "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
118     GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
119         "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR  }");
120 /* *INDENT-ON* */
121
122 static void
123 _reset_history (GstVaDeinterlace * self)
124 {
125   gint i;
126
127   for (i = 0; i < self->hcount; i++)
128     gst_buffer_unref (self->history[i]);
129   self->hcount = 0;
130 }
131
132 static void
133 gst_va_deinterlace_dispose (GObject * object)
134 {
135   GstVaDeinterlace *self = GST_VA_DEINTERLACE (object);
136
137   _reset_history (self);
138
139   G_OBJECT_CLASS (parent_class)->dispose (object);
140 }
141
142 static void
143 gst_va_deinterlace_set_property (GObject * object, guint prop_id,
144     const GValue * value, GParamSpec * pspec)
145 {
146   GstVaDeinterlace *self = GST_VA_DEINTERLACE (object);
147   guint method;
148
149   GST_OBJECT_LOCK (object);
150   switch (prop_id) {
151     case GST_VA_FILTER_PROP_DEINTERLACE_METHOD:
152       method = g_value_get_enum (value);
153       if (method != self->method) {
154         self->method = method;
155         g_atomic_int_set (&self->rebuild_filters, TRUE);
156       }
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160       break;
161   }
162   GST_OBJECT_UNLOCK (object);
163 }
164
165 static void
166 gst_va_deinterlace_get_property (GObject * object, guint prop_id,
167     GValue * value, GParamSpec * pspec)
168 {
169   GstVaDeinterlace *self = GST_VA_DEINTERLACE (object);
170
171   GST_OBJECT_LOCK (object);
172   switch (prop_id) {
173     case GST_VA_FILTER_PROP_DEINTERLACE_METHOD:{
174       g_value_set_enum (value, self->method);
175       break;
176     }
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179       break;
180   }
181   GST_OBJECT_UNLOCK (object);
182 }
183
184 static GstFlowReturn
185 gst_va_deinterlace_submit_input_buffer (GstBaseTransform * trans,
186     gboolean is_discont, GstBuffer * input)
187 {
188   GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
189   GstVaDeinterlace *self = GST_VA_DEINTERLACE (trans);
190   GstBuffer *buf, *inbuf;
191   GstFlowReturn ret;
192   gint i;
193
194   /* Let baseclass handle QoS first */
195   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->submit_input_buffer (trans,
196       is_discont, input);
197   if (ret != GST_FLOW_OK)
198     return ret;
199
200   if (gst_base_transform_is_passthrough (trans))
201     return ret;
202
203   /* at this moment, baseclass must hold queued_buf */
204   g_assert (trans->queued_buf != NULL);
205
206   /* Check if we can use this buffer directly. If not, copy this into
207    * our fallback buffer */
208   buf = trans->queued_buf;
209   trans->queued_buf = NULL;
210
211   ret = gst_va_base_transform_import_buffer (btrans, buf, &inbuf);
212   if (ret != GST_FLOW_OK)
213     return ret;
214
215   gst_buffer_unref (buf);
216
217   if (self->hcount < self->hdepth) {
218     self->history[self->hcount++] = inbuf;
219   } else {
220     gst_clear_buffer (&self->history[0]);
221     for (i = 0; i + 1 < self->hcount; i++)
222       self->history[i] = self->history[i + 1];
223     self->history[i] = inbuf;
224   }
225
226   if (self->history[self->hcurr])
227     self->curr_field = FIRST_FIELD;
228
229   return ret;
230 }
231
232 static void
233 _build_filter (GstVaDeinterlace * self)
234 {
235   GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
236   guint i, num_caps;
237   VAProcFilterCapDeinterlacing *caps;
238   guint32 num_forward_references;
239
240   caps = gst_va_filter_get_filter_caps (btrans->filter,
241       VAProcFilterDeinterlacing, &num_caps);
242   if (!caps)
243     return;
244
245   for (i = 0; i < num_caps; i++) {
246     if (caps[i].type != self->method)
247       continue;
248
249     if (gst_va_filter_add_deinterlace_buffer (btrans->filter, self->method,
250             &num_forward_references, &self->num_backward_references)) {
251       self->hdepth = num_forward_references + self->num_backward_references + 1;
252       if (self->hdepth > 8) {
253         GST_ELEMENT_ERROR (self, STREAM, FAILED,
254             ("Pipeline requires too many references: (%u forward, %u backward)",
255                 num_forward_references, self->num_backward_references), (NULL));
256       }
257       GST_INFO_OBJECT (self, "References for method: %u forward / %u backward",
258           num_forward_references, self->num_backward_references);
259       self->hcurr = num_forward_references;
260       return;
261     }
262   }
263
264   GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS,
265       ("Invalid deinterlacing method: %d", self->method), (NULL));
266 }
267
268 static void
269 gst_va_deinterlace_rebuild_filters (GstVaDeinterlace * self)
270 {
271   GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
272
273   if (!g_atomic_int_get (&self->rebuild_filters))
274     return;
275
276   _reset_history (self);
277   gst_va_filter_drop_filter_buffers (btrans->filter);
278   _build_filter (self);
279
280   /* extra number of buffers for propose_allocation */
281   if (self->hdepth > btrans->extra_min_buffers) {
282     btrans->extra_min_buffers = self->hdepth;
283     gst_base_transform_reconfigure_sink (GST_BASE_TRANSFORM (self));
284   }
285
286   g_atomic_int_set (&self->rebuild_filters, FALSE);
287 }
288
289 static gboolean
290 gst_va_deinterlace_set_info (GstVaBaseTransform * btrans, GstCaps * incaps,
291     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
292 {
293   GstBaseTransform *trans = GST_BASE_TRANSFORM (btrans);
294   GstVaDeinterlace *self = GST_VA_DEINTERLACE (btrans);
295
296   switch (GST_VIDEO_INFO_INTERLACE_MODE (in_info)) {
297     case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
298       /* Nothing to do */
299       gst_base_transform_set_passthrough (trans, TRUE);
300       return TRUE;
301       break;
302     case GST_VIDEO_INTERLACE_MODE_ALTERNATE:
303     case GST_VIDEO_INTERLACE_MODE_FIELDS:
304       GST_ERROR_OBJECT (self, "Unsupported interlace mode.");
305       return FALSE;
306       break;
307     default:
308       break;
309   }
310
311   /* Calculate expected buffer duration. We might need to reference this value
312    * when buffer duration is unknown */
313   if (GST_VIDEO_INFO_FPS_N (in_info) > 0 && GST_VIDEO_INFO_FPS_D (in_info) > 0) {
314     self->default_duration =
315         gst_util_uint64_scale_int (GST_SECOND, GST_VIDEO_INFO_FPS_D (in_info),
316         GST_VIDEO_INFO_FPS_N (in_info));
317   } else {
318     /* Assume 25 fps. We need this for reporting latency at least  */
319     self->default_duration = gst_util_uint64_scale_int (GST_SECOND, 1, 25);
320   }
321
322   if (gst_va_filter_set_video_info (btrans->filter, in_info, out_info)) {
323     g_atomic_int_set (&self->rebuild_filters, TRUE);
324     gst_base_transform_set_passthrough (trans, FALSE);
325     gst_va_deinterlace_rebuild_filters (self);
326
327     return TRUE;
328   }
329
330   return FALSE;
331 }
332
333 static void
334 gst_va_deinterlace_before_transform (GstBaseTransform * trans,
335     GstBuffer * inbuf)
336 {
337   GstVaDeinterlace *self = GST_VA_DEINTERLACE (trans);
338   GstClockTime ts, stream_time;
339
340   ts = GST_BUFFER_TIMESTAMP (inbuf);
341   stream_time =
342       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, ts);
343
344   GST_TRACE_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
345
346   if (GST_CLOCK_TIME_IS_VALID (stream_time))
347     gst_object_sync_values (GST_OBJECT (self), stream_time);
348
349   gst_va_deinterlace_rebuild_filters (self);
350 }
351
352 static void
353 _set_field (GstVaDeinterlace * self, guint32 * surface_flags)
354 {
355   GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
356
357   if (trans->segment.rate < 0) {
358     if ((self->curr_field == FIRST_FIELD
359             && (*surface_flags & VA_TOP_FIELD_FIRST))
360         || (self->curr_field == SECOND_FIELD
361             && (*surface_flags & VA_BOTTOM_FIELD_FIRST))) {
362       *surface_flags |= VA_BOTTOM_FIELD;
363     } else {
364       *surface_flags |= VA_TOP_FIELD;
365     }
366   } else {
367     if ((self->curr_field == FIRST_FIELD
368             && (*surface_flags & VA_BOTTOM_FIELD_FIRST))
369         || (self->curr_field == SECOND_FIELD
370             && (*surface_flags & VA_TOP_FIELD_FIRST))) {
371       *surface_flags |= VA_BOTTOM_FIELD;
372     } else {
373       *surface_flags |= VA_TOP_FIELD;
374     }
375   }
376 }
377
378 static GstFlowReturn
379 gst_va_deinterlace_transform (GstBaseTransform * trans, GstBuffer * inbuf,
380     GstBuffer * outbuf)
381 {
382   GstVaDeinterlace *self = GST_VA_DEINTERLACE (trans);
383   GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
384   GstFlowReturn res = GST_FLOW_OK;
385   GstVaSample src, dst;
386   GstVideoInfo *info = &btrans->in_info;
387   VASurfaceID forward_references[8], backward_references[8];
388   guint i, surface_flags;
389
390   if (G_UNLIKELY (!btrans->negotiated))
391     goto unknown_format;
392
393   g_assert (self->curr_field == FIRST_FIELD
394       || self->curr_field == SECOND_FIELD);
395
396   surface_flags = gst_va_buffer_get_surface_flags (inbuf, info);
397   if (surface_flags != VA_FRAME_PICTURE)
398     _set_field (self, &surface_flags);
399
400   GST_TRACE_OBJECT (self, "Processing %d field (flags = %u): %" GST_PTR_FORMAT,
401       self->curr_field, surface_flags, inbuf);
402
403   for (i = 0; i < self->hcurr; i++) {
404     forward_references[i] =
405         gst_va_buffer_get_surface (self->history[self->hcurr - i - 1]);
406   }
407   for (i = 0; i < self->num_backward_references; i++) {
408     backward_references[i] =
409         gst_va_buffer_get_surface (self->history[self->hcurr + i + 1]);
410   }
411
412   /* *INDENT-OFF* */
413   src = (GstVaSample) {
414     .buffer = inbuf,
415     .flags = surface_flags,
416     .forward_references = forward_references,
417     .num_forward_references = self->hcurr,
418     .backward_references = backward_references,
419     .num_backward_references = self->num_backward_references,
420   };
421   dst = (GstVaSample) {
422     .buffer = outbuf,
423   };
424   /* *INDENT-ON* */
425
426   if (!gst_va_filter_process (btrans->filter, &src, &dst)) {
427     gst_buffer_set_flags (outbuf, GST_BUFFER_FLAG_CORRUPTED);
428     res = GST_BASE_TRANSFORM_FLOW_DROPPED;
429   }
430
431   return res;
432
433   /* ERRORS */
434 unknown_format:
435   {
436     GST_ELEMENT_ERROR (self, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
437     return GST_FLOW_NOT_NEGOTIATED;
438   }
439 }
440
441 static GstFlowReturn
442 gst_va_deinterlace_generate_output (GstBaseTransform * trans,
443     GstBuffer ** outbuf)
444 {
445   GstVaDeinterlace *self = GST_VA_DEINTERLACE (trans);
446   GstFlowReturn ret;
447   GstBuffer *inbuf, *buf = NULL;
448
449   if (gst_base_transform_is_passthrough (trans)) {
450     return GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans,
451         outbuf);
452   }
453
454   *outbuf = NULL;
455
456   if (self->curr_field == FINISHED)
457     return GST_FLOW_OK;
458
459   inbuf = self->history[self->hcurr];
460   if (!inbuf)
461     return GST_FLOW_OK;
462
463   if (!self->history[self->hdepth - 1])
464     return GST_FLOW_OK;
465
466   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->prepare_output_buffer (trans,
467       inbuf, &buf);
468   if (ret != GST_FLOW_OK || !buf) {
469     GST_WARNING_OBJECT (self, "Could not get buffer from pool: %s",
470         gst_flow_get_name (ret));
471     return ret;
472   }
473
474   ret = gst_va_deinterlace_transform (trans, inbuf, buf);
475   if (ret != GST_FLOW_OK) {
476     gst_buffer_unref (buf);
477     return ret;
478   }
479
480   if (!GST_BUFFER_PTS_IS_VALID (inbuf)) {
481     GST_LOG_OBJECT (self, "Input buffer timestamp is unknown");
482   } else {
483     GstClockTime duration;
484
485     if (GST_BUFFER_DURATION_IS_VALID (inbuf))
486       duration = GST_BUFFER_DURATION (inbuf) / 2;
487     else
488       duration = self->default_duration / 2;
489
490     GST_BUFFER_DURATION (buf) = duration;
491     if (self->curr_field == SECOND_FIELD)
492       GST_BUFFER_PTS (buf) = GST_BUFFER_PTS (buf) + duration;
493   }
494
495   *outbuf = buf;
496
497   GST_TRACE_OBJECT (self, "Pushing %" GST_PTR_FORMAT, buf);
498
499   if (self->curr_field == FIRST_FIELD)
500     self->curr_field = SECOND_FIELD;
501   else if (self->curr_field == SECOND_FIELD)
502     self->curr_field = FINISHED;
503
504   return ret;
505 }
506
507 static GstCaps *
508 gst_va_deinterlace_remove_interlace (GstCaps * caps)
509 {
510   GstStructure *st;
511   gint i, n;
512   GstCaps *res;
513   GstCapsFeatures *f;
514
515   res = gst_caps_new_empty ();
516
517   n = gst_caps_get_size (caps);
518   for (i = 0; i < n; i++) {
519     st = gst_caps_get_structure (caps, i);
520     f = gst_caps_get_features (caps, i);
521
522     /* If this is already expressed by the existing caps
523      * skip this structure */
524     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
525       continue;
526
527     st = gst_structure_copy (st);
528     gst_structure_remove_fields (st, "interlace-mode", "field-order",
529         "framerate", NULL);
530
531     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
532   }
533
534   return res;
535 }
536
537 static GstCaps *
538 gst_va_deinterlace_transform_caps (GstBaseTransform * trans,
539     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
540 {
541   GstVaDeinterlace *self = GST_VA_DEINTERLACE (trans);
542   GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
543   GstCaps *ret, *filter_caps;
544
545   GST_DEBUG_OBJECT (self,
546       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
547       (direction == GST_PAD_SINK) ? "sink" : "src");
548
549   filter_caps = gst_va_base_transform_get_filter_caps (btrans);
550   if (filter_caps && !gst_caps_can_intersect (caps, filter_caps)) {
551     ret = gst_caps_ref (caps);
552     goto bail;
553   }
554
555   ret = gst_va_deinterlace_remove_interlace (caps);
556
557 bail:
558   if (filter) {
559     GstCaps *intersection;
560
561     intersection =
562         gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
563     gst_caps_unref (ret);
564     ret = intersection;
565   }
566
567   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
568
569   return ret;
570 }
571
572 static GstCaps *
573 gst_va_deinterlace_fixate_caps (GstBaseTransform * trans,
574     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
575 {
576   GstVaDeinterlace *self = GST_VA_DEINTERLACE (trans);
577   GstCapsFeatures *out_f;
578   GstStructure *in_s, *out_s;
579   gint fps_n, fps_d;
580   const gchar *in_interlace_mode, *out_interlace_mode;
581
582   GST_DEBUG_OBJECT (self,
583       "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
584       GST_PTR_FORMAT, othercaps, caps);
585
586   othercaps = gst_caps_truncate (othercaps);
587   othercaps = gst_caps_make_writable (othercaps);
588
589   if (direction == GST_PAD_SRC) {
590     othercaps = gst_caps_fixate (othercaps);
591     goto bail;
592   }
593
594   in_s = gst_caps_get_structure (caps, 0);
595   in_interlace_mode = gst_structure_get_string (in_s, "interlace-mode");
596
597   out_s = gst_caps_get_structure (othercaps, 0);
598
599   if (g_strcmp0 ("progressive", in_interlace_mode) == 0) {
600     /* Just forward interlace-mode=progressive and framerate
601      * By this way, basetransform will enable passthrough for non-interlaced
602      * stream */
603     const GValue *framerate = gst_structure_get_value (in_s, "framerate");
604     gst_structure_set_value (out_s, "framerate", framerate);
605     gst_structure_set (out_s, "interlace-mode", G_TYPE_STRING, "progressive",
606         NULL);
607
608     goto bail;
609   }
610
611   out_f = gst_caps_get_features (othercaps, 0);
612   out_interlace_mode = gst_structure_get_string (out_s, "interlace-mode");
613
614   if ((!out_interlace_mode
615           || (g_strcmp0 ("progressive", out_interlace_mode) == 0))
616       && (gst_caps_features_contains (out_f, GST_CAPS_FEATURE_MEMORY_VA)
617           || gst_caps_features_contains (out_f, GST_CAPS_FEATURE_MEMORY_DMABUF)
618           || gst_caps_features_contains (out_f,
619               GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY))) {
620     gst_structure_set (out_s, "interlace-mode", G_TYPE_STRING, "progressive",
621         NULL);
622
623     if (gst_structure_get_fraction (in_s, "framerate", &fps_n, &fps_d)) {
624       fps_n *= 2;
625       gst_structure_set (out_s, "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
626           NULL);
627     }
628   } else {
629     /* if caps features aren't supported, just forward interlace-mode
630      * and framerate */
631     const GValue *framerate = gst_structure_get_value (in_s, "framerate");
632     gst_structure_set_value (out_s, "framerate", framerate);
633     gst_structure_set (out_s, "interlace-mode", G_TYPE_STRING,
634         in_interlace_mode, NULL);
635   }
636
637 bail:
638   GST_DEBUG_OBJECT (self, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
639
640   return othercaps;
641 }
642
643 static gboolean
644 gst_va_deinterlace_query (GstBaseTransform * trans, GstPadDirection direction,
645     GstQuery * query)
646 {
647   GstVaDeinterlace *self = GST_VA_DEINTERLACE (trans);
648
649   if (direction == GST_PAD_SRC && GST_QUERY_TYPE (query) == GST_QUERY_LATENCY
650       && !gst_base_transform_is_passthrough (trans)) {
651     GstPad *peer;
652     GstClockTime latency, min, max;
653     gboolean res = FALSE;
654     gboolean live;
655
656     peer = gst_pad_get_peer (GST_BASE_TRANSFORM_SINK_PAD (trans));
657     if (!peer)
658       return FALSE;
659
660     res = gst_pad_query (peer, query);
661     gst_object_unref (peer);
662     if (!res)
663       return FALSE;
664
665     gst_query_parse_latency (query, &live, &min, &max);
666
667     GST_DEBUG_OBJECT (self, "Peer latency: min %" GST_TIME_FORMAT " max %"
668         GST_TIME_FORMAT, GST_TIME_ARGS (min), GST_TIME_ARGS (max));
669
670     /* add our own latency: number of fields + history depth */
671     latency = (2 + self->hdepth) * self->default_duration;
672
673     GST_DEBUG_OBJECT (self, "Our latency: min %" GST_TIME_FORMAT ", max %"
674         GST_TIME_FORMAT, GST_TIME_ARGS (latency), GST_TIME_ARGS (latency));
675
676     min += latency;
677     if (max != GST_CLOCK_TIME_NONE)
678       max += latency;
679
680     GST_DEBUG_OBJECT (self, "Calculated total latency : min %" GST_TIME_FORMAT
681         " max %" GST_TIME_FORMAT, GST_TIME_ARGS (min), GST_TIME_ARGS (max));
682
683     gst_query_set_latency (query, live, min, max);
684
685     return TRUE;
686   }
687
688   return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
689       query);
690 }
691
692 static void
693 gst_va_deinterlace_class_init (gpointer g_class, gpointer class_data)
694 {
695   GstCaps *doc_caps, *sink_caps = NULL, *src_caps = NULL;
696   GstPadTemplate *sink_pad_templ, *src_pad_templ;
697   GObjectClass *object_class = G_OBJECT_CLASS (g_class);
698   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
699   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
700   GstVaBaseTransformClass *btrans_class = GST_VA_BASE_TRANSFORM_CLASS (g_class);
701   GstVaDisplay *display;
702   GstVaFilter *filter;
703   struct CData *cdata = class_data;
704   gchar *long_name;
705
706   parent_class = g_type_class_peek_parent (g_class);
707
708   btrans_class->render_device_path = g_strdup (cdata->render_device_path);
709
710   if (cdata->description) {
711     long_name = g_strdup_printf ("VA-API Deinterlacer in %s",
712         cdata->description);
713   } else {
714     long_name = g_strdup ("VA-API Deinterlacer");
715   }
716
717   gst_element_class_set_metadata (element_class, long_name,
718       "Filter/Effect/Video/Deinterlace",
719       "VA-API based deinterlacer", "Víctor Jáquez <vjaquez@igalia.com>");
720
721   display = gst_va_display_drm_new_from_path (btrans_class->render_device_path);
722   filter = gst_va_filter_new (display);
723
724   if (gst_va_filter_open (filter)) {
725     src_caps = gst_va_filter_get_caps (filter);
726     /* adds any to enable passthrough */
727     {
728       GstCaps *any_caps = gst_caps_new_empty_simple ("video/x-raw");
729       gst_caps_set_features_simple (any_caps, gst_caps_features_new_any ());
730       src_caps = gst_caps_merge (src_caps, any_caps);
731     }
732   } else {
733     src_caps = gst_caps_from_string (caps_str);
734   }
735
736   sink_caps = gst_va_deinterlace_remove_interlace (src_caps);
737
738   doc_caps = gst_caps_from_string (caps_str);
739
740   sink_pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
741       sink_caps);
742   gst_element_class_add_pad_template (element_class, sink_pad_templ);
743   gst_pad_template_set_documentation_caps (sink_pad_templ,
744       gst_caps_ref (doc_caps));
745
746   src_pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
747       src_caps);
748   gst_element_class_add_pad_template (element_class, src_pad_templ);
749   gst_pad_template_set_documentation_caps (src_pad_templ,
750       gst_caps_ref (doc_caps));
751   gst_caps_unref (doc_caps);
752
753   gst_caps_unref (src_caps);
754   gst_caps_unref (sink_caps);
755
756   object_class->dispose = gst_va_deinterlace_dispose;
757   object_class->set_property = gst_va_deinterlace_set_property;
758   object_class->get_property = gst_va_deinterlace_get_property;
759
760   trans_class->transform_caps =
761       GST_DEBUG_FUNCPTR (gst_va_deinterlace_transform_caps);
762   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_va_deinterlace_fixate_caps);
763   trans_class->before_transform =
764       GST_DEBUG_FUNCPTR (gst_va_deinterlace_before_transform);
765   trans_class->transform = GST_DEBUG_FUNCPTR (gst_va_deinterlace_transform);
766   trans_class->submit_input_buffer =
767       GST_DEBUG_FUNCPTR (gst_va_deinterlace_submit_input_buffer);
768   trans_class->generate_output =
769       GST_DEBUG_FUNCPTR (gst_va_deinterlace_generate_output);
770   trans_class->query = GST_DEBUG_FUNCPTR (gst_va_deinterlace_query);
771
772   trans_class->transform_ip_on_passthrough = FALSE;
773
774   btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_va_deinterlace_set_info);
775
776   gst_va_filter_install_deinterlace_properties (filter, object_class);
777
778   g_free (long_name);
779   g_free (cdata->description);
780   g_free (cdata->render_device_path);
781   g_free (cdata);
782   gst_object_unref (filter);
783   gst_object_unref (display);
784 }
785
786 static void
787 gst_va_deinterlace_init (GTypeInstance * instance, gpointer g_class)
788 {
789   GstVaDeinterlace *self = GST_VA_DEINTERLACE (instance);
790   GParamSpec *pspec;
791
792   pspec = g_object_class_find_property (g_class, "method");
793   g_assert (pspec);
794   self->method = g_value_get_enum (g_param_spec_get_default_value (pspec));
795 }
796
797 static gpointer
798 _register_debug_category (gpointer data)
799 {
800   GST_DEBUG_CATEGORY_INIT (gst_va_deinterlace_debug, "vadeinterlace", 0,
801       "VA Video Deinterlace");
802
803   return NULL;
804 }
805
806 gboolean
807 gst_va_deinterlace_register (GstPlugin * plugin, GstVaDevice * device,
808     guint rank)
809 {
810   static GOnce debug_once = G_ONCE_INIT;
811   GType type;
812   GTypeInfo type_info = {
813     .class_size = sizeof (GstVaDeinterlaceClass),
814     .class_init = gst_va_deinterlace_class_init,
815     .instance_size = sizeof (GstVaDeinterlace),
816     .instance_init = gst_va_deinterlace_init,
817   };
818   struct CData *cdata;
819   gboolean ret;
820   gchar *type_name, *feature_name;
821
822   g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
823   g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
824
825   cdata = g_new (struct CData, 1);
826   cdata->description = NULL;
827   cdata->render_device_path = g_strdup (device->render_device_path);
828
829   type_info.class_data = cdata;
830
831   type_name = g_strdup ("GstVaDeinterlace");
832   feature_name = g_strdup ("vadeinterlace");
833
834   /* The first postprocessor to be registered should use a constant
835    * name, like vadeinterlace, for any additional postprocessors, we
836    * create unique names, using inserting the render device name. */
837   if (g_type_from_name (type_name)) {
838     gchar *basename = g_path_get_basename (device->render_device_path);
839     g_free (type_name);
840     g_free (feature_name);
841     type_name = g_strdup_printf ("GstVa%sDeinterlace", basename);
842     feature_name = g_strdup_printf ("va%sdeinterlace", basename);
843     cdata->description = basename;
844
845     /* lower rank for non-first device */
846     if (rank > 0)
847       rank--;
848   }
849
850   g_once (&debug_once, _register_debug_category, NULL);
851
852   type = g_type_register_static (GST_TYPE_VA_BASE_TRANSFORM, type_name,
853       &type_info, 0);
854
855   ret = gst_element_register (plugin, feature_name, rank, type);
856
857   g_free (type_name);
858   g_free (feature_name);
859
860   return ret;
861 }