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