vaapipostproc: fix transform caps.
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapipostproc.c
1 /*
2  *  gstvaapipostproc.c - VA-API video postprocessing
3  *
4  *  Copyright (C) 2012-2013 Intel Corporation
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * SECTION:gstvaapipostproc
24  * @short_description: A video postprocessing filter
25  *
26  * vaapipostproc consists in various postprocessing algorithms to be
27  * applied to VA surfaces. So far, only basic bob deinterlacing is
28  * implemented.
29  */
30
31 #include "gst/vaapi/sysdeps.h"
32 #include <gst/video/video.h>
33 #include <gst/video/videocontext.h>
34
35 #include "gstvaapipostproc.h"
36 #include "gstvaapipluginutil.h"
37 #include "gstvaapivideobuffer.h"
38 #if GST_CHECK_VERSION(1,0,0)
39 #include "gstvaapivideobufferpool.h"
40 #include "gstvaapivideomemory.h"
41 #endif
42
43 #define GST_PLUGIN_NAME "vaapipostproc"
44 #define GST_PLUGIN_DESC "A video postprocessing filter"
45
46 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc);
47 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
48
49 /* Default templates */
50 static const char gst_vaapipostproc_sink_caps_str[] =
51     GST_VAAPI_SURFACE_CAPS ", "
52     GST_CAPS_INTERLACED_MODES "; "
53 #if GST_CHECK_VERSION(1,0,0)
54     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
55 #else
56     "video/x-raw-yuv, "
57     "width  = " GST_VIDEO_SIZE_RANGE ", "
58     "height = " GST_VIDEO_SIZE_RANGE ", "
59     "framerate = " GST_VIDEO_FPS_RANGE ", "
60 #endif
61     GST_CAPS_INTERLACED_MODES;
62
63 static const char gst_vaapipostproc_src_caps_str[] =
64     GST_VAAPI_SURFACE_CAPS ", "
65     GST_CAPS_INTERLACED_FALSE;
66
67 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
68     GST_STATIC_PAD_TEMPLATE(
69         "sink",
70         GST_PAD_SINK,
71         GST_PAD_ALWAYS,
72         GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
73
74 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
75     GST_STATIC_PAD_TEMPLATE(
76         "src",
77         GST_PAD_SRC,
78         GST_PAD_ALWAYS,
79         GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
80
81 /* GstImplementsInterface interface */
82 #if !GST_CHECK_VERSION(1,0,0)
83 static gboolean
84 gst_vaapipostproc_implements_interface_supported(
85     GstImplementsInterface *iface,
86     GType                   type
87 )
88 {
89     return (type == GST_TYPE_VIDEO_CONTEXT);
90 }
91
92 static void
93 gst_vaapipostproc_implements_iface_init(GstImplementsInterfaceClass *iface)
94 {
95     iface->supported = gst_vaapipostproc_implements_interface_supported;
96 }
97 #endif
98
99 /* GstVideoContext interface */
100 static void
101 gst_vaapipostproc_set_video_context(
102     GstVideoContext *context,
103     const gchar     *type,
104     const GValue    *value
105 )
106 {
107     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(context);
108
109     gst_vaapi_set_display(type, value, &postproc->display);
110
111     if (postproc->uploader)
112         gst_vaapi_uploader_ensure_display(postproc->uploader, postproc->display);
113 }
114
115 static void
116 gst_video_context_interface_init(GstVideoContextInterface *iface)
117 {
118     iface->set_context = gst_vaapipostproc_set_video_context;
119 }
120
121 #define GstVideoContextClass GstVideoContextInterface
122 G_DEFINE_TYPE_WITH_CODE(
123     GstVaapiPostproc,
124     gst_vaapipostproc,
125     GST_TYPE_BASE_TRANSFORM,
126 #if !GST_CHECK_VERSION(1,0,0)
127     G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
128                           gst_vaapipostproc_implements_iface_init);
129 #endif
130     G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
131                           gst_video_context_interface_init))
132
133 enum {
134     PROP_0,
135
136     PROP_FORMAT,
137     PROP_WIDTH,
138     PROP_HEIGHT,
139     PROP_FORCE_ASPECT_RATIO,
140     PROP_DEINTERLACE_MODE,
141     PROP_DEINTERLACE_METHOD,
142 };
143
144 #define DEFAULT_FORMAT                  GST_VIDEO_FORMAT_ENCODED
145 #define DEFAULT_DEINTERLACE_MODE        GST_VAAPI_DEINTERLACE_MODE_AUTO
146 #define DEFAULT_DEINTERLACE_METHOD      GST_VAAPI_DEINTERLACE_METHOD_BOB
147
148 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
149     gst_vaapi_deinterlace_mode_get_type()
150
151 static GType
152 gst_vaapi_deinterlace_mode_get_type(void)
153 {
154     static GType deinterlace_mode_type = 0;
155
156     static const GEnumValue mode_types[] = {
157         { GST_VAAPI_DEINTERLACE_MODE_AUTO,
158           "Auto detection", "auto" },
159         { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
160           "Force deinterlacing", "interlaced" },
161         { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
162           "Never deinterlace", "disabled" },
163         { 0, NULL, NULL },
164     };
165
166     if (!deinterlace_mode_type) {
167         deinterlace_mode_type =
168             g_enum_register_static("GstVaapiDeinterlaceMode", mode_types);
169     }
170     return deinterlace_mode_type;
171 }
172
173 static GstVaapiFilterOpInfo *
174 find_filter_op(GPtrArray *filter_ops, GstVaapiFilterOp op)
175 {
176     guint i;
177
178     if (filter_ops) {
179         for (i = 0; i < filter_ops->len; i++) {
180             GstVaapiFilterOpInfo * const filter_op =
181                 g_ptr_array_index(filter_ops, i);
182             if (filter_op->op == op)
183                 return filter_op;
184         }
185     }
186     return NULL;
187 }
188
189 static inline gboolean
190 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
191 {
192     return gst_vaapi_ensure_display(postproc, GST_VAAPI_DISPLAY_TYPE_ANY,
193         &postproc->display);
194 }
195
196 static gboolean
197 gst_vaapipostproc_ensure_uploader(GstVaapiPostproc *postproc)
198 {
199     if (!gst_vaapipostproc_ensure_display(postproc))
200         return FALSE;
201
202     if (!postproc->uploader) {
203         postproc->uploader = gst_vaapi_uploader_new(postproc->display);
204         if (!postproc->uploader)
205             return FALSE;
206     }
207
208     if (!gst_vaapi_uploader_ensure_display(postproc->uploader,
209             postproc->display))
210         return FALSE;
211     return TRUE;
212 }
213
214 static gboolean
215 gst_vaapipostproc_ensure_uploader_caps(GstVaapiPostproc *postproc)
216 {
217 #if !GST_CHECK_VERSION(1,0,0)
218     if (postproc->is_raw_yuv && !gst_vaapi_uploader_ensure_caps(
219             postproc->uploader, postproc->sinkpad_caps, NULL))
220         return FALSE;
221 #endif
222     return TRUE;
223 }
224
225 static gboolean
226 gst_vaapipostproc_ensure_filter(GstVaapiPostproc *postproc)
227 {
228     if (postproc->filter)
229         return TRUE;
230
231     if (!gst_vaapipostproc_ensure_display(postproc))
232         return FALSE;
233
234     postproc->filter = gst_vaapi_filter_new(postproc->display);
235     if (!postproc->filter)
236         return FALSE;
237     return TRUE;
238 }
239
240 static gboolean
241 gst_vaapipostproc_ensure_filter_caps(GstVaapiPostproc *postproc)
242 {
243     if (!gst_vaapipostproc_ensure_filter(postproc))
244         return FALSE;
245
246     postproc->filter_ops = gst_vaapi_filter_get_operations(postproc->filter);
247     if (!postproc->filter_ops)
248         return FALSE;
249
250     postproc->filter_formats = gst_vaapi_filter_get_formats(postproc->filter);
251     if (!postproc->filter_formats)
252         return FALSE;
253     return TRUE;
254 }
255
256 static gboolean
257 gst_vaapipostproc_create(GstVaapiPostproc *postproc)
258 {
259     if (!gst_vaapipostproc_ensure_display(postproc))
260         return FALSE;
261     if (!gst_vaapipostproc_ensure_uploader(postproc))
262         return FALSE;
263     if (!gst_vaapipostproc_ensure_uploader_caps(postproc))
264         return FALSE;
265     if (gst_vaapipostproc_ensure_filter(postproc))
266         postproc->use_vpp = TRUE;
267     return TRUE;
268 }
269
270 static void
271 gst_vaapipostproc_destroy_filter(GstVaapiPostproc *postproc)
272 {
273     if (postproc->filter_formats) {
274         g_array_unref(postproc->filter_formats);
275         postproc->filter_formats = NULL;
276     }
277
278     if (postproc->filter_ops) {
279         g_ptr_array_unref(postproc->filter_ops);
280         postproc->filter_ops = NULL;
281     }
282     gst_vaapi_filter_replace(&postproc->filter, NULL);
283 }
284
285 static void
286 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
287 {
288 #if GST_CHECK_VERSION(1,0,0)
289     g_clear_object(&postproc->sinkpad_buffer_pool);
290 #endif
291     g_clear_object(&postproc->uploader);
292     gst_vaapipostproc_destroy_filter(postproc);
293     gst_vaapi_display_replace(&postproc->display, NULL);
294
295     gst_caps_replace(&postproc->allowed_sinkpad_caps, NULL);
296     gst_caps_replace(&postproc->sinkpad_caps, NULL);
297     gst_caps_replace(&postproc->allowed_srcpad_caps, NULL);
298     gst_caps_replace(&postproc->srcpad_caps,  NULL);
299 }
300
301 static gboolean
302 gst_vaapipostproc_start(GstBaseTransform *trans)
303 {
304     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
305
306     if (!gst_vaapipostproc_ensure_display(postproc))
307         return FALSE;
308     return TRUE;
309 }
310
311 static gboolean
312 gst_vaapipostproc_stop(GstBaseTransform *trans)
313 {
314     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
315
316     gst_vaapi_display_replace(&postproc->display, NULL);
317     return TRUE;
318 }
319
320 static gboolean
321 is_interlaced_buffer(GstVaapiPostproc *postproc, GstBuffer *buf)
322 {
323     if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
324         return FALSE;
325
326     switch (GST_VIDEO_INFO_INTERLACE_MODE(&postproc->sinkpad_info)) {
327     case GST_VIDEO_INTERLACE_MODE_MIXED:
328 #if GST_CHECK_VERSION(1,0,0)
329         if (!GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
330             return FALSE;
331 #else
332         if (GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_PROGRESSIVE))
333             return FALSE;
334 #endif
335         break;
336     default:
337         break;
338     }
339     return TRUE;
340 }
341
342 static GstBuffer *
343 create_output_buffer(GstVaapiPostproc *postproc)
344 {
345     GstBuffer *outbuf;
346
347     /* Create a raw VA video buffer without GstVaapiVideoMeta attached
348        to it yet, as this will be done next in the transform() hook */
349     outbuf = gst_vaapi_video_buffer_new_empty();
350     if (!outbuf)
351         goto error_create_buffer;
352
353 #if !GST_CHECK_VERSION(1,0,0)
354     gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
355 #endif
356     return outbuf;
357
358     /* ERRORS */
359 error_create_buffer:
360     {
361         GST_ERROR("failed to create output video buffer");
362         return NULL;
363     }
364 }
365
366 static inline void
367 append_output_buffer_metadata(GstBuffer *outbuf, GstBuffer *inbuf, guint flags)
368 {
369     gst_buffer_copy_into(outbuf, inbuf, flags |
370         GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY,
371         0, -1);
372 }
373
374 static GstVaapiDeinterlaceMethod
375 get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
376 {
377     switch (deint_method) {
378     case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
379         deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
380         break;
381     default:
382         /* Default to basic "bob" for all others */
383         deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
384         break;
385     }
386     return deint_method;
387 }
388
389 static gboolean
390 set_best_deint_method(GstVaapiPostproc *postproc, guint flags,
391     GstVaapiDeinterlaceMethod *deint_method_ptr)
392 {
393     GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
394     gboolean success;
395
396     for (;;) {
397         success = gst_vaapi_filter_set_deinterlacing(postproc->filter,
398             deint_method, flags);
399         if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
400             break;
401         deint_method = get_next_deint_method(deint_method);
402     }
403     *deint_method_ptr = deint_method;
404     return success;
405 }
406
407 static GstFlowReturn
408 gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
409     GstBuffer *outbuf)
410 {
411     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
412     GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
413     GstVaapiSurface *inbuf_surface, *outbuf_surface;
414     GstVaapiFilterStatus status;
415     GstClockTime timestamp;
416     GstFlowReturn ret;
417     GstBuffer *fieldbuf;
418     guint flags, deint_flags;
419     gboolean tff, deint;
420
421     /* Validate filters */
422     if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
423         !gst_vaapi_filter_set_format(postproc->filter, postproc->format))
424         return GST_FLOW_NOT_SUPPORTED;
425
426     inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
427     if (!inbuf_meta)
428         goto error_invalid_buffer;
429     inbuf_surface = gst_vaapi_video_meta_get_surface(inbuf_meta);
430
431     timestamp  = GST_BUFFER_TIMESTAMP(inbuf);
432     tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
433     deint      = is_interlaced_buffer(postproc, inbuf);
434
435     flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
436         ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
437           GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
438
439     /* First field */
440     if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
441         fieldbuf = create_output_buffer(postproc);
442         if (!fieldbuf)
443             goto error_create_buffer;
444
445         outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
446         if (!outbuf_meta)
447             goto error_create_meta;
448         outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
449
450         if (deint) {
451             GstVaapiDeinterlaceMethod deint_method;
452             deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
453             if (!set_best_deint_method(postproc, flags, &deint_method))
454                 goto error_op_deinterlace;
455             if (deint_method != postproc->deinterlace_method) {
456                 GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
457                           postproc->deinterlace_method, deint_method);
458                 postproc->deinterlace_method = deint_method;
459             }
460         }
461
462         status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
463             outbuf_surface, flags);
464         if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
465             goto error_process_vpp;
466
467         gst_buffer_set_vaapi_video_meta(fieldbuf, outbuf_meta);
468         gst_vaapi_video_meta_unref(outbuf_meta);
469
470         GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
471         GST_BUFFER_DURATION(fieldbuf)  = postproc->field_duration;
472         ret = gst_pad_push(trans->srcpad, fieldbuf);
473         if (ret != GST_FLOW_OK)
474             goto error_push_buffer;
475     }
476     fieldbuf = NULL;
477
478     /* Second field */
479     outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
480     if (!outbuf_meta)
481         goto error_create_meta;
482     outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
483
484     if (deint) {
485         deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
486         if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
487                 postproc->deinterlace_method, deint_flags))
488             goto error_op_deinterlace;
489     }
490
491     status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
492         outbuf_surface, flags);
493     if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
494         goto error_process_vpp;
495
496     if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
497         gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
498     else {
499         GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
500         GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
501     }
502     gst_buffer_set_vaapi_video_meta(outbuf, outbuf_meta);
503     gst_vaapi_video_meta_unref(outbuf_meta);
504     return GST_FLOW_OK;
505
506     /* ERRORS */
507 error_invalid_buffer:
508     {
509         GST_ERROR("failed to validate source buffer");
510         return GST_FLOW_ERROR;
511     }
512 error_create_buffer:
513     {
514         GST_ERROR("failed to create output buffer");
515         return GST_FLOW_ERROR;
516     }
517 error_create_meta:
518     {
519         GST_ERROR("failed to create new output buffer meta");
520         gst_buffer_replace(&fieldbuf, NULL);
521         gst_vaapi_video_meta_unref(outbuf_meta);
522         return GST_FLOW_ERROR;
523     }
524 error_op_deinterlace:
525     {
526         GST_ERROR("failed to apply deinterlacing filter");
527         gst_buffer_replace(&fieldbuf, NULL);
528         gst_vaapi_video_meta_unref(outbuf_meta);
529         return GST_FLOW_NOT_SUPPORTED;
530     }
531 error_process_vpp:
532     {
533         GST_ERROR("failed to apply VPP filters (error %d)", status);
534         gst_buffer_replace(&fieldbuf, NULL);
535         gst_vaapi_video_meta_unref(outbuf_meta);
536         return GST_FLOW_ERROR;
537     }
538 error_push_buffer:
539     {
540         if (ret != GST_FLOW_FLUSHING)
541             GST_ERROR("failed to push output buffer to video sink");
542         return GST_FLOW_ERROR;
543     }
544 }
545
546 static GstFlowReturn
547 gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
548     GstBuffer *outbuf)
549 {
550     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
551     GstVaapiVideoMeta *meta;
552     GstClockTime timestamp;
553     GstFlowReturn ret;
554     GstBuffer *fieldbuf;
555     guint fieldbuf_flags, outbuf_flags, flags;
556     gboolean tff, deint;
557
558     meta = gst_buffer_get_vaapi_video_meta(inbuf);
559     if (!meta)
560         goto error_invalid_buffer;
561
562     timestamp  = GST_BUFFER_TIMESTAMP(inbuf);
563     tff        = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
564     deint      = is_interlaced_buffer(postproc, inbuf);
565
566     flags = gst_vaapi_video_meta_get_render_flags(meta) &
567         ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
568           GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
569
570     /* First field */
571     fieldbuf = create_output_buffer(postproc);
572     if (!fieldbuf)
573         goto error_create_buffer;
574     append_output_buffer_metadata(fieldbuf, inbuf, 0);
575
576     meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
577     fieldbuf_flags = flags;
578     fieldbuf_flags |= deint ? (
579         tff ?
580         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
581         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
582         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
583     gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
584
585     GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
586     GST_BUFFER_DURATION(fieldbuf)  = postproc->field_duration;
587     ret = gst_pad_push(trans->srcpad, fieldbuf);
588     if (ret != GST_FLOW_OK)
589         goto error_push_buffer;
590
591     /* Second field */
592     append_output_buffer_metadata(outbuf, inbuf, 0);
593
594     meta = gst_buffer_get_vaapi_video_meta(outbuf);
595     outbuf_flags = flags;
596     outbuf_flags |= deint ? (
597         tff ?
598         GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
599         GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
600         GST_VAAPI_PICTURE_STRUCTURE_FRAME;
601     gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
602
603     GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
604     GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
605     return GST_FLOW_OK;
606
607     /* ERRORS */
608 error_invalid_buffer:
609     {
610         GST_ERROR("failed to validate source buffer");
611         return GST_FLOW_ERROR;
612     }
613 error_create_buffer:
614     {
615         GST_ERROR("failed to create output buffer");
616         return GST_FLOW_EOS;
617     }
618 error_push_buffer:
619     {
620         if (ret != GST_FLOW_FLUSHING)
621             GST_ERROR("failed to push output buffer to video sink");
622         return GST_FLOW_EOS;
623     }
624 }
625
626 static GstFlowReturn
627 gst_vaapipostproc_passthrough(GstBaseTransform *trans, GstBuffer *inbuf,
628     GstBuffer *outbuf)
629 {
630     GstVaapiVideoMeta *meta;
631
632     /* No video processing needed, simply copy buffer metadata */
633     meta = gst_buffer_get_vaapi_video_meta(inbuf);
634     if (!meta)
635         goto error_invalid_buffer;
636
637     append_output_buffer_metadata(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
638     return GST_FLOW_OK;
639
640     /* ERRORS */
641 error_invalid_buffer:
642     {
643         GST_ERROR("failed to validate source buffer");
644         return GST_FLOW_ERROR;
645     }
646 }
647
648 static gboolean
649 is_deinterlace_enabled(GstVaapiPostproc *postproc, GstVideoInfo *vip)
650 {
651     gboolean deinterlace;
652
653     switch (postproc->deinterlace_mode) {
654     case GST_VAAPI_DEINTERLACE_MODE_AUTO:
655         deinterlace = GST_VIDEO_INFO_IS_INTERLACED(vip);
656         break;
657     case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
658         deinterlace = TRUE;
659         break;
660     default:
661         deinterlace = FALSE;
662         break;
663     }
664     return deinterlace;
665 }
666
667 static gboolean
668 video_info_changed(GstVideoInfo *old_vip, GstVideoInfo *new_vip)
669 {
670     if (GST_VIDEO_INFO_FORMAT(old_vip) != GST_VIDEO_INFO_FORMAT(new_vip))
671         return TRUE;
672     if (GST_VIDEO_INFO_INTERLACE_MODE(old_vip) !=
673         GST_VIDEO_INFO_INTERLACE_MODE(new_vip))
674         return TRUE;
675     if (GST_VIDEO_INFO_WIDTH(old_vip) != GST_VIDEO_INFO_WIDTH(new_vip))
676         return TRUE;
677     if (GST_VIDEO_INFO_HEIGHT(old_vip) != GST_VIDEO_INFO_HEIGHT(new_vip))
678         return TRUE;
679     return FALSE;
680 }
681
682 static gboolean
683 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
684     gboolean *caps_changed_ptr)
685 {
686     GstVideoInfo vi;
687     gboolean deinterlace;
688
689     if (!gst_video_info_from_caps(&vi, caps))
690         return FALSE;
691
692     if (video_info_changed(&vi, &postproc->sinkpad_info))
693         postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
694
695     deinterlace = is_deinterlace_enabled(postproc, &vi);
696     if (deinterlace)
697         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
698     postproc->field_duration = gst_util_uint64_scale(
699         GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
700         (1 + deinterlace) * GST_VIDEO_INFO_FPS_N(&vi));
701
702     postproc->is_raw_yuv = GST_VIDEO_INFO_IS_YUV(&vi);
703 #if !GST_CHECK_VERSION(1,0,0)
704     if (postproc->is_raw_yuv) {
705         /* Ensure the uploader is set up for upstream allocated buffers */
706         GstVaapiUploader * const uploader = postproc->uploader;
707         if (!gst_vaapi_uploader_ensure_display(uploader, postproc->display))
708             return FALSE;
709         if (!gst_vaapi_uploader_ensure_caps(uploader, caps, NULL))
710             return FALSE;
711     }
712 #endif
713     return TRUE;
714 }
715
716 static gboolean
717 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps,
718     gboolean *caps_changed_ptr)
719 {
720     GstVideoInfo vi;
721
722     if (!gst_video_info_from_caps(&vi, caps))
723         return FALSE;
724
725     if (video_info_changed(&vi, &postproc->srcpad_info))
726         postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
727
728     if (postproc->format != GST_VIDEO_INFO_FORMAT(&postproc->sinkpad_info))
729         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
730
731     if ((postproc->width || postproc->height) &&
732         postproc->width != GST_VIDEO_INFO_WIDTH(&postproc->sinkpad_info) &&
733         postproc->height != GST_VIDEO_INFO_HEIGHT(&postproc->sinkpad_info))
734         postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
735     return TRUE;
736 }
737
738 static gboolean
739 ensure_allowed_sinkpad_caps(GstVaapiPostproc *postproc)
740 {
741     GstCaps *out_caps, *yuv_caps;
742
743     if (postproc->allowed_sinkpad_caps)
744         return TRUE;
745
746     /* Create VA caps */
747     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS ", "
748         GST_CAPS_INTERLACED_MODES);
749     if (!out_caps) {
750         GST_ERROR("failed to create VA sink caps");
751         return FALSE;
752     }
753
754     /* Append YUV caps */
755     if (gst_vaapipostproc_ensure_uploader(postproc)) {
756         yuv_caps = gst_vaapi_uploader_get_caps(postproc->uploader);
757         if (yuv_caps)
758             gst_caps_append(out_caps, gst_caps_ref(yuv_caps));
759         else
760             GST_WARNING("failed to create YUV sink caps");
761     }
762     postproc->allowed_sinkpad_caps = out_caps;
763
764     /* XXX: append VA/VPP filters */
765     return TRUE;
766 }
767
768 /* Build list of supported formats */
769 static gboolean
770 build_format_list_value(GArray *formats, GValue *out_value)
771 {
772     GValue v_format = { 0, };
773     guint i;
774 #if GST_CHECK_VERSION(1,0,0)
775     const gchar *str;
776
777     g_value_init(out_value, GST_TYPE_LIST);
778
779     g_value_init(&v_format, G_TYPE_STRING);
780     g_value_set_string(&v_format, "encoded");
781     gst_value_list_append_value(out_value, &v_format);
782
783     for (i = 0; i < formats->len; i++) {
784         GstVideoFormat const format =
785             g_array_index(formats, GstVideoFormat, i);
786
787         str = gst_vaapi_video_format_to_string(format);
788         if (!str)
789             continue;
790         g_value_set_string(&v_format, str);
791         gst_value_list_append_value(out_value, &v_format);
792     }
793 #else
794     guint32 fourcc;
795
796     g_value_init(out_value, GST_TYPE_LIST);
797     g_value_init(&v_format, GST_TYPE_FOURCC);
798     for (i = 0; i < formats->len; i++) {
799         GstVideoFormat const format =
800             g_array_index(formats, GstVideoFormat, i);
801
802         fourcc = gst_video_format_to_fourcc(format);
803         if (!fourcc)
804             continue;
805         gst_value_set_fourcc(&v_format, fourcc);
806         gst_value_list_append_value(out_value, &v_format);
807     }
808 #endif
809
810     g_value_unset(&v_format);
811     return TRUE;
812 }
813
814 /* Fixup output caps so that to reflect the supported set of pixel formats */
815 static GstCaps *
816 expand_allowed_srcpad_caps(GstVaapiPostproc *postproc, GstCaps *caps)
817 {
818     GValue value = { 0, };
819     guint i, num_structures;
820     gboolean had_filter;
821
822     had_filter = postproc->filter != NULL;
823     if (!had_filter && !gst_vaapipostproc_ensure_filter(postproc))
824         goto cleanup;
825     if (!gst_vaapipostproc_ensure_filter_caps(postproc))
826         goto cleanup;
827
828     /* Reset "format" field for each structure */
829     if (!build_format_list_value(postproc->filter_formats, &value))
830         goto cleanup;
831
832     num_structures = gst_caps_get_size(caps);
833     for (i = 0; i < num_structures; i++) {
834         GstStructure * const structure = gst_caps_get_structure(caps, i);
835         if (!structure)
836             continue;
837         gst_structure_set_value(structure, "format", &value);
838     }
839     g_value_unset(&value);
840
841 cleanup:
842     if (!had_filter)
843         gst_vaapipostproc_destroy_filter(postproc);
844     return caps;
845 }
846
847 static gboolean
848 ensure_allowed_srcpad_caps(GstVaapiPostproc *postproc)
849 {
850     GstCaps *out_caps;
851
852     if (postproc->allowed_srcpad_caps)
853         return TRUE;
854
855     /* Create initial caps from pad template */
856     out_caps = gst_caps_from_string(gst_vaapipostproc_src_caps_str);
857     if (!out_caps) {
858         GST_ERROR("failed to create VA src caps");
859         return FALSE;
860     }
861
862     postproc->allowed_srcpad_caps =
863         expand_allowed_srcpad_caps(postproc, out_caps);
864     return postproc->allowed_srcpad_caps != NULL;
865 }
866
867 static void
868 find_best_size(GstVaapiPostproc *postproc, GstVideoInfo *vip,
869     guint *width_ptr, guint *height_ptr)
870 {
871     guint width, height;
872
873     width  = GST_VIDEO_INFO_WIDTH(vip);
874     height = GST_VIDEO_INFO_HEIGHT(vip);
875     if (postproc->width && postproc->height) {
876         width = postproc->width;
877         height = postproc->height;
878     }
879     else if (postproc->keep_aspect) {
880         const gdouble ratio  = (gdouble)width / height;
881         if (postproc->width) {
882             width = postproc->width;
883             height = postproc->width / ratio;
884         }
885         else if (postproc->height) {
886             height = postproc->height;
887             width = postproc->height * ratio;
888         }
889     }
890     else if (postproc->width)
891         width = postproc->width;
892     else if (postproc->height)
893         height = postproc->height;
894
895     *width_ptr = width;
896     *height_ptr = height;
897 }
898
899 static GstCaps *
900 gst_vaapipostproc_transform_caps_impl(GstBaseTransform *trans,
901     GstPadDirection direction, GstCaps *caps)
902 {
903     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
904     GstVideoInfo vi;
905     GstVideoFormat format;
906     GstCaps *out_caps;
907     guint width, height;
908
909     /* Generate the sink pad caps, that could be fixated afterwards */
910     if (direction == GST_PAD_SRC) {
911         if (!ensure_allowed_sinkpad_caps(postproc))
912             return NULL;
913         return gst_caps_ref(postproc->allowed_sinkpad_caps);
914     }
915
916     /* Generate complete set of src pad caps if non-fixated sink pad
917        caps are provided */
918     if (!gst_caps_is_fixed(caps)) {
919         if (!ensure_allowed_srcpad_caps(postproc))
920             return NULL;
921         return gst_caps_ref(postproc->allowed_srcpad_caps);
922     }
923
924     /* Generate the expected src pad caps, from the current fixated
925        sink pad caps */
926     if (!gst_video_info_from_caps(&vi, caps))
927         return NULL;
928
929     // Set double framerate in interlaced mode
930     if (is_deinterlace_enabled(postproc, &vi)) {
931         gint fps_n = GST_VIDEO_INFO_FPS_N(&vi);
932         gint fps_d = GST_VIDEO_INFO_FPS_D(&vi);
933         if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
934             return NULL;
935         GST_VIDEO_INFO_FPS_N(&vi) = fps_n;
936         GST_VIDEO_INFO_FPS_D(&vi) = fps_d;
937     }
938
939     // Signal the other pad that we only generate progressive frames
940     GST_VIDEO_INFO_INTERLACE_MODE(&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
941
942     // Update size from user-specified parameters
943     format = GST_VIDEO_FORMAT_ENCODED;
944     find_best_size(postproc, &vi, &width, &height);
945     gst_video_info_set_format(&vi, format, width, height);
946
947     /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
948        reconstruct suitable caps for "encoded" video formats */
949     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
950     if (!out_caps)
951         return NULL;
952
953     gst_caps_set_simple(out_caps,
954         "type", G_TYPE_STRING, "vaapi",
955         "opengl", G_TYPE_BOOLEAN, USE_GLX,
956         "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH(&vi),
957         "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT(&vi),
958         "framerate", GST_TYPE_FRACTION, GST_VIDEO_INFO_FPS_N(&vi),
959             GST_VIDEO_INFO_FPS_D(&vi),
960         "pixel-aspect-ratio", GST_TYPE_FRACTION, GST_VIDEO_INFO_PAR_N(&vi),
961             GST_VIDEO_INFO_PAR_D(&vi),
962         NULL);
963
964     gst_caps_set_interlaced(out_caps, &vi);
965     return out_caps;
966 }
967
968 #if GST_CHECK_VERSION(1,0,0)
969 static GstCaps *
970 gst_vaapipostproc_transform_caps(GstBaseTransform *trans,
971     GstPadDirection direction, GstCaps *caps, GstCaps *filter)
972 {
973     GstCaps *out_caps;
974
975     caps = gst_vaapipostproc_transform_caps_impl(trans, direction, caps);
976     if (caps && filter) {
977         out_caps = gst_caps_intersect_full(caps, filter,
978             GST_CAPS_INTERSECT_FIRST);
979         gst_caps_unref(caps);
980         return out_caps;
981     }
982     return caps;
983 }
984 #else
985 #define gst_vaapipostproc_transform_caps \
986     gst_vaapipostproc_transform_caps_impl
987 #endif
988
989 #if GST_CHECK_VERSION(1,0,0)
990 typedef gsize GstBaseTransformSizeType;
991 #else
992 typedef guint GstBaseTransformSizeType;
993 #endif
994
995 static gboolean
996 gst_vaapipostproc_transform_size(GstBaseTransform *trans,
997     GstPadDirection direction, GstCaps *caps, GstBaseTransformSizeType size,
998     GstCaps *othercaps, GstBaseTransformSizeType *othersize)
999 {
1000     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1001
1002     if (direction == GST_PAD_SINK || !postproc->is_raw_yuv)
1003         *othersize = 0;
1004     else
1005         *othersize = size;
1006     return TRUE;
1007 }
1008
1009 static GstBuffer *
1010 get_source_buffer(GstVaapiPostproc *postproc, GstBuffer *inbuf)
1011 {
1012     GstVaapiVideoMeta *meta;
1013     GstBuffer *outbuf;
1014 #if GST_CHECK_VERSION(1,0,0)
1015     GstVideoFrame src_frame, out_frame;
1016 #endif
1017
1018     meta = gst_buffer_get_vaapi_video_meta(inbuf);
1019     if (meta)
1020         return gst_buffer_ref(inbuf);
1021
1022 #if GST_CHECK_VERSION(1,0,0)
1023     if (!postproc->is_raw_yuv)
1024         goto error_invalid_buffer;
1025
1026     if (!postproc->sinkpad_buffer_pool)
1027         goto error_no_pool;
1028
1029     if (!gst_buffer_pool_set_active(postproc->sinkpad_buffer_pool, TRUE))
1030         goto error_active_pool;
1031
1032     outbuf = NULL;
1033     if (gst_buffer_pool_acquire_buffer(postproc->sinkpad_buffer_pool,
1034             &outbuf, NULL) != GST_FLOW_OK)
1035         goto error_create_buffer;
1036
1037     if (!gst_video_frame_map(&src_frame, &postproc->sinkpad_info, inbuf,
1038             GST_MAP_READ))
1039         goto error_map_src_buffer;
1040
1041     if (!gst_video_frame_map(&out_frame, &postproc->sinkpad_info, outbuf,
1042             GST_MAP_WRITE))
1043         goto error_map_dst_buffer;
1044
1045     if (!gst_video_frame_copy(&out_frame, &src_frame))
1046         goto error_copy_buffer;
1047
1048     gst_video_frame_unmap(&out_frame);
1049     gst_video_frame_unmap(&src_frame);
1050     gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
1051     return outbuf;
1052
1053     /* ERRORS */
1054 error_invalid_buffer:
1055     {
1056         GST_ERROR("failed to validate source buffer");
1057         return NULL;
1058     }
1059 error_no_pool:
1060     {
1061         GST_ERROR("no buffer pool was negotiated");
1062         return NULL;
1063     }
1064 error_active_pool:
1065     {
1066         GST_ERROR("failed to activate buffer pool");
1067         return NULL;
1068     }
1069 error_map_dst_buffer:
1070     {
1071         gst_video_frame_unmap(&src_frame);
1072         // fall-through
1073     }
1074 error_map_src_buffer:
1075     {
1076         GST_ERROR("failed to map buffer");
1077         gst_buffer_unref(outbuf);
1078         return NULL;
1079     }
1080 #else
1081     outbuf = gst_vaapi_uploader_get_buffer(postproc->uploader);
1082     if (!outbuf)
1083         goto error_create_buffer;
1084     if (!gst_vaapi_uploader_process(postproc->uploader, inbuf, outbuf))
1085         goto error_copy_buffer;
1086
1087     gst_buffer_copy_metadata(outbuf, inbuf,
1088         GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS);
1089     return outbuf;
1090 #endif
1091
1092     /* ERRORS */
1093 error_create_buffer:
1094     {
1095         GST_ERROR("failed to create buffer");
1096         return NULL;
1097     }
1098 error_copy_buffer:
1099     {
1100         GST_ERROR("failed to upload buffer to VA surface");
1101 #if GST_CHECK_VERSION(1,0,0)
1102         gst_video_frame_unmap(&out_frame);
1103         gst_video_frame_unmap(&src_frame);
1104 #endif
1105         gst_buffer_unref(outbuf);
1106         return NULL;
1107     }
1108 }
1109
1110 static GstFlowReturn
1111 gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
1112     GstBuffer *outbuf)
1113 {
1114     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1115     GstBuffer *buf;
1116     GstFlowReturn ret;
1117
1118     buf = get_source_buffer(postproc, inbuf);
1119     if (!buf)
1120         return GST_FLOW_ERROR;
1121
1122     ret = GST_FLOW_NOT_SUPPORTED;
1123     if (postproc->flags) {
1124         /* Use VA/VPP extensions to process this frame */
1125         if (postproc->use_vpp &&
1126             postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1127             ret = gst_vaapipostproc_process_vpp(trans, buf, outbuf);
1128             if (ret != GST_FLOW_NOT_SUPPORTED)
1129                 goto done;
1130             GST_WARNING("unsupported VPP filters. Disabling");
1131             postproc->use_vpp = FALSE;
1132         }
1133
1134         /* Only append picture structure meta data (top/bottom field) */
1135         if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1136             ret = gst_vaapipostproc_process(trans, buf, outbuf);
1137             if (ret != GST_FLOW_NOT_SUPPORTED)
1138                 goto done;
1139         }
1140     }
1141
1142     /* Fallback: passthrough to the downstream element as is */
1143     ret = gst_vaapipostproc_passthrough(trans, buf, outbuf);
1144
1145 done:
1146     gst_buffer_unref(buf);
1147     return ret;
1148 }
1149
1150 static GstFlowReturn
1151 gst_vaapipostproc_prepare_output_buffer(GstBaseTransform *trans,
1152     GstBuffer *inbuf,
1153 #if !GST_CHECK_VERSION(1,0,0)
1154     gint size, GstCaps *caps,
1155 #endif
1156     GstBuffer **outbuf_ptr)
1157 {
1158     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1159
1160     *outbuf_ptr = create_output_buffer(postproc);
1161     return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
1162 }
1163
1164 static gboolean
1165 ensure_sinkpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
1166 {
1167 #if GST_CHECK_VERSION(1,0,0)
1168     GstBufferPool *pool;
1169     GstCaps *pool_caps;
1170     GstStructure *config;
1171     GstVideoInfo vi;
1172     gboolean need_pool;
1173
1174     if (!gst_vaapipostproc_ensure_display(postproc))
1175         return FALSE;
1176
1177     if (postproc->sinkpad_buffer_pool) {
1178         config = gst_buffer_pool_get_config(postproc->sinkpad_buffer_pool);
1179         gst_buffer_pool_config_get_params(config, &pool_caps, NULL, NULL, NULL);
1180         need_pool = !gst_caps_is_equal(caps, pool_caps);
1181         gst_structure_free(config);
1182         if (!need_pool)
1183             return TRUE;
1184         g_clear_object(&postproc->sinkpad_buffer_pool);
1185         postproc->sinkpad_buffer_size = 0;
1186     }
1187
1188     pool = gst_vaapi_video_buffer_pool_new(postproc->display);
1189     if (!pool)
1190         goto error_create_pool;
1191
1192     gst_video_info_init(&vi);
1193     gst_video_info_from_caps(&vi, caps);
1194     if (GST_VIDEO_INFO_FORMAT(&vi) == GST_VIDEO_FORMAT_ENCODED) {
1195         GST_DEBUG("assume sink pad buffer pool format is NV12");
1196         gst_video_info_set_format(&vi, GST_VIDEO_FORMAT_NV12,
1197             GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
1198     }
1199     postproc->sinkpad_buffer_size = vi.size;
1200
1201     config = gst_buffer_pool_get_config(pool);
1202     gst_buffer_pool_config_set_params(config, caps,
1203         postproc->sinkpad_buffer_size, 0, 0);
1204     gst_buffer_pool_config_add_option(config,
1205         GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META);
1206     gst_buffer_pool_config_add_option(config,
1207         GST_BUFFER_POOL_OPTION_VIDEO_META);
1208     if (!gst_buffer_pool_set_config(pool, config))
1209         goto error_pool_config;
1210     postproc->sinkpad_buffer_pool = pool;
1211     return TRUE;
1212
1213     /* ERRORS */
1214 error_create_pool:
1215     {
1216         GST_ERROR("failed to create buffer pool");
1217         return FALSE;
1218     }
1219 error_pool_config:
1220     {
1221         GST_ERROR("failed to reset buffer pool config");
1222         gst_object_unref(pool);
1223         return FALSE;
1224     }
1225 #else
1226     return TRUE;
1227 #endif
1228 }
1229
1230 static gboolean
1231 ensure_srcpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
1232 {
1233     GstVideoInfo vi;
1234     GstVaapiVideoPool *pool;
1235
1236     gst_video_info_init(&vi);
1237     gst_video_info_from_caps(&vi, caps);
1238     gst_video_info_set_format(&vi, postproc->format,
1239         GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
1240
1241     if (!video_info_changed(&vi, &postproc->filter_pool_info))
1242         return TRUE;
1243     postproc->filter_pool_info = vi;
1244
1245     pool = gst_vaapi_surface_pool_new(postproc->display,
1246         &postproc->filter_pool_info);
1247     if (!pool)
1248         return FALSE;
1249
1250     gst_vaapi_video_pool_replace(&postproc->filter_pool, pool);
1251     gst_vaapi_video_pool_unref(pool);
1252     return TRUE;
1253 }
1254
1255 static gboolean
1256 gst_vaapipostproc_set_caps(GstBaseTransform *trans, GstCaps *caps,
1257     GstCaps *out_caps)
1258 {
1259     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1260     gboolean caps_changed = FALSE;
1261
1262     if (!gst_vaapipostproc_update_sink_caps(postproc, caps, &caps_changed))
1263         return FALSE;
1264     if (!gst_vaapipostproc_update_src_caps(postproc, out_caps, &caps_changed))
1265         return FALSE;
1266
1267     if (caps_changed) {
1268         gst_vaapipostproc_destroy(postproc);
1269         gst_caps_replace(&postproc->sinkpad_caps, caps);
1270         gst_caps_replace(&postproc->srcpad_caps, out_caps);
1271         if (!gst_vaapipostproc_create(postproc))
1272             return FALSE;
1273     }
1274
1275     if (!ensure_sinkpad_buffer_pool(postproc, caps))
1276         return FALSE;
1277     if (!ensure_srcpad_buffer_pool(postproc, out_caps))
1278         return FALSE;
1279     return TRUE;
1280 }
1281
1282 static gboolean
1283 gst_vaapipostproc_query(GstBaseTransform *trans, GstPadDirection direction,
1284     GstQuery *query)
1285 {
1286     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1287
1288     GST_INFO_OBJECT(trans, "query type `%s'", GST_QUERY_TYPE_NAME(query));
1289
1290     if (gst_vaapi_reply_to_query(query, postproc->display)) {
1291         GST_DEBUG("sharing display %p", postproc->display);
1292         return TRUE;
1293     }
1294
1295     return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
1296         trans, direction, query);
1297 }
1298
1299 #if GST_CHECK_VERSION(1,0,0)
1300 static gboolean
1301 gst_vaapipostproc_propose_allocation(GstBaseTransform *trans,
1302     GstQuery *decide_query, GstQuery *query)
1303 {
1304     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1305     GstCaps *caps = NULL;
1306     gboolean need_pool;
1307
1308     /* Let vaapidecode allocate the video buffers */
1309     if (!postproc->is_raw_yuv)
1310         return FALSE;
1311
1312     gst_query_parse_allocation(query, &caps, &need_pool);
1313
1314     if (need_pool) {
1315         if (!caps)
1316             goto error_no_caps;
1317         if (!ensure_sinkpad_buffer_pool(postproc, caps))
1318             return FALSE;
1319         gst_query_add_allocation_pool(query, postproc->sinkpad_buffer_pool,
1320             postproc->sinkpad_buffer_size, 0, 0);
1321     }
1322
1323     gst_query_add_allocation_meta(query,
1324         GST_VAAPI_VIDEO_META_API_TYPE, NULL);
1325     gst_query_add_allocation_meta(query,
1326         GST_VIDEO_META_API_TYPE, NULL);
1327     gst_query_add_allocation_meta(query,
1328         GST_VIDEO_CROP_META_API_TYPE, NULL);
1329     gst_query_add_allocation_meta(query,
1330         GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
1331     return TRUE;
1332
1333     /* ERRORS */
1334 error_no_caps:
1335     {
1336         GST_ERROR("no caps specified");
1337         return FALSE;
1338     }
1339 }
1340 #endif
1341
1342 static void
1343 gst_vaapipostproc_finalize(GObject *object)
1344 {
1345     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1346
1347     gst_vaapipostproc_destroy(postproc);
1348
1349     G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
1350 }
1351
1352 static void
1353 gst_vaapipostproc_set_property(
1354     GObject      *object,
1355     guint         prop_id,
1356     const GValue *value,
1357     GParamSpec   *pspec
1358 )
1359 {
1360     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1361
1362     switch (prop_id) {
1363     case PROP_FORMAT:
1364         postproc->format = g_value_get_enum(value);
1365         break;
1366     case PROP_WIDTH:
1367         postproc->width = g_value_get_uint(value);
1368         break;
1369     case PROP_HEIGHT:
1370         postproc->height = g_value_get_uint(value);
1371         break;
1372     case PROP_FORCE_ASPECT_RATIO:
1373         postproc->keep_aspect = g_value_get_boolean(value);
1374         break;
1375     case PROP_DEINTERLACE_MODE:
1376         postproc->deinterlace_mode = g_value_get_enum(value);
1377         break;
1378     case PROP_DEINTERLACE_METHOD:
1379         postproc->deinterlace_method = g_value_get_enum(value);
1380         break;
1381     default:
1382         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1383         break;
1384     }
1385 }
1386
1387 static void
1388 gst_vaapipostproc_get_property(
1389     GObject    *object,
1390     guint       prop_id,
1391     GValue     *value,
1392     GParamSpec *pspec
1393 )
1394 {
1395     GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1396
1397     switch (prop_id) {
1398     case PROP_FORMAT:
1399         g_value_set_enum(value, postproc->format);
1400         break;
1401     case PROP_WIDTH:
1402         g_value_set_uint(value, postproc->width);
1403         break;
1404     case PROP_HEIGHT:
1405         g_value_set_uint(value, postproc->height);
1406         break;
1407     case PROP_FORCE_ASPECT_RATIO:
1408         g_value_set_boolean(value, postproc->keep_aspect);
1409         break;
1410     case PROP_DEINTERLACE_MODE:
1411         g_value_set_enum(value, postproc->deinterlace_mode);
1412         break;
1413     case PROP_DEINTERLACE_METHOD:
1414         g_value_set_enum(value, postproc->deinterlace_method);
1415         break;
1416     default:
1417         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1418         break;
1419     }
1420 }
1421
1422 static void
1423 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
1424 {
1425     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
1426     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
1427     GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
1428     GstPadTemplate *pad_template;
1429     GPtrArray *filter_ops;
1430     GstVaapiFilterOpInfo *filter_op;
1431
1432     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
1433                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1434
1435     object_class->finalize      = gst_vaapipostproc_finalize;
1436     object_class->set_property  = gst_vaapipostproc_set_property;
1437     object_class->get_property  = gst_vaapipostproc_get_property;
1438     trans_class->start          = gst_vaapipostproc_start;
1439     trans_class->stop           = gst_vaapipostproc_stop;
1440     trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1441     trans_class->transform_size = gst_vaapipostproc_transform_size;
1442     trans_class->transform      = gst_vaapipostproc_transform;
1443     trans_class->set_caps       = gst_vaapipostproc_set_caps;
1444     trans_class->query          = gst_vaapipostproc_query;
1445
1446 #if GST_CHECK_VERSION(1,0,0)
1447     trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1448 #endif
1449
1450     trans_class->prepare_output_buffer =
1451         gst_vaapipostproc_prepare_output_buffer;
1452
1453     gst_element_class_set_static_metadata(element_class,
1454         "VA-API video postprocessing",
1455         "Filter/Converter/Video",
1456         GST_PLUGIN_DESC,
1457         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1458
1459     /* sink pad */
1460     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
1461     gst_element_class_add_pad_template(element_class, pad_template);
1462
1463     /* src pad */
1464     pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
1465     gst_element_class_add_pad_template(element_class, pad_template);
1466
1467     /**
1468      * GstVaapiPostproc:deinterlace-mode:
1469      *
1470      * This selects whether the deinterlacing should always be applied or if
1471      * they should only be applied on content that has the "interlaced" flag
1472      * on the caps.
1473      */
1474     g_object_class_install_property
1475         (object_class,
1476          PROP_DEINTERLACE_MODE,
1477          g_param_spec_enum("deinterlace-mode",
1478                            "Deinterlace mode",
1479                            "Deinterlace mode to use",
1480                            GST_VAAPI_TYPE_DEINTERLACE_MODE,
1481                            DEFAULT_DEINTERLACE_MODE,
1482                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1483
1484     /**
1485      * GstVaapiPostproc:deinterlace-method:
1486      *
1487      * This selects the deinterlacing method to apply.
1488      */
1489     g_object_class_install_property
1490         (object_class,
1491          PROP_DEINTERLACE_METHOD,
1492          g_param_spec_enum("deinterlace-method",
1493                            "Deinterlace method",
1494                            "Deinterlace method to use",
1495                            GST_VAAPI_TYPE_DEINTERLACE_METHOD,
1496                            DEFAULT_DEINTERLACE_METHOD,
1497                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1498
1499     filter_ops = gst_vaapi_filter_get_operations(NULL);
1500     if (!filter_ops)
1501         return;
1502
1503     /**
1504      * GstVaapiPostproc:format:
1505      *
1506      * The forced output pixel format, expressed as a #GstVideoFormat.
1507      */
1508     filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
1509     if (filter_op)
1510         g_object_class_install_property(object_class,
1511             PROP_FORMAT, filter_op->pspec);
1512
1513     /**
1514      * GstVaapiPostproc:width:
1515      *
1516      * The forced output width in pixels. If set to zero, the width is
1517      * calculated from the height if aspect ration is preserved, or
1518      * inherited from the sink caps width
1519      */
1520     g_object_class_install_property
1521         (object_class,
1522          PROP_WIDTH,
1523          g_param_spec_uint("width",
1524                            "Width",
1525                            "Forced output width",
1526                            0, G_MAXINT, 0,
1527                            G_PARAM_READWRITE));
1528
1529     /**
1530      * GstVaapiPostproc:height:
1531      *
1532      * The forced output height in pixels. If set to zero, the height
1533      * is calculated from the width if aspect ration is preserved, or
1534      * inherited from the sink caps height
1535      */
1536     g_object_class_install_property
1537         (object_class,
1538          PROP_HEIGHT,
1539          g_param_spec_uint("height",
1540                            "Height",
1541                            "Forced output height",
1542                            0, G_MAXINT, 0,
1543                            G_PARAM_READWRITE));
1544
1545     /**
1546      * GstVaapiPostproc:force-aspect-ratio:
1547      *
1548      * When enabled, scaling respects video aspect ratio; when
1549      * disabled, the video is distorted to fit the width and height
1550      * properties.
1551      */
1552     g_object_class_install_property
1553         (object_class,
1554          PROP_FORCE_ASPECT_RATIO,
1555          g_param_spec_boolean("force-aspect-ratio",
1556                               "Force aspect ratio",
1557                               "When enabled, scaling will respect original aspect ratio",
1558                               TRUE,
1559                               G_PARAM_READWRITE));
1560
1561     g_ptr_array_unref(filter_ops);
1562 }
1563
1564 static void
1565 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
1566 {
1567     postproc->format                    = DEFAULT_FORMAT;
1568     postproc->deinterlace_mode          = DEFAULT_DEINTERLACE_MODE;
1569     postproc->deinterlace_method        = DEFAULT_DEINTERLACE_METHOD;
1570     postproc->field_duration            = GST_CLOCK_TIME_NONE;
1571     postproc->keep_aspect               = TRUE;
1572
1573     gst_video_info_init(&postproc->sinkpad_info);
1574     gst_video_info_init(&postproc->srcpad_info);
1575     gst_video_info_init(&postproc->filter_pool_info);
1576 }