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