Properly set opengl support in caps
[vaapi:stormers-gstreamer-vaapi.git] / gst / vaapi / gstvaapiconvert.c
1 /*
2  *  gstvaapiconvert.c - VA-API video converter
3  *
4  *  gstreamer-vaapi (C) 2010-2011 Splitted-Desktop Systems
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:gstvaapiconvert
24  * @short_description: A VA-API based video pixels format converter
25  *
26  * vaapiconvert converts from raw YUV pixels to surfaces suitable for
27  * the vaapisink element.
28  */
29
30 #include "config.h"
31
32 #include <gst/gst.h>
33 #include <gst/video/video.h>
34 #include <gst/video/videocontext.h>
35 #include <gst/vaapi/gstvaapivideosink.h>
36 #include <gst/vaapi/gstvaapivideobuffer.h>
37
38 #if USE_VAAPI_GLX
39 #include <gst/vaapi/gstvaapivideobuffer_glx.h>
40 #define gst_vaapi_video_buffer_new_from_pool gst_vaapi_video_buffer_glx_new_from_pool
41 #define gst_vaapi_video_buffer_new_from_buffer gst_vaapi_video_buffer_glx_new_from_buffer
42 #endif
43
44 #include "gstvaapipluginutil.h"
45 #include "gstvaapiconvert.h"
46
47 #define GST_PLUGIN_NAME "vaapiconvert"
48 #define GST_PLUGIN_DESC "A VA-API based video pixels format converter"
49
50 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapiconvert);
51 #define GST_CAT_DEFAULT gst_debug_vaapiconvert
52
53 /* ElementFactory information */
54 static const GstElementDetails gst_vaapiconvert_details =
55     GST_ELEMENT_DETAILS(
56         "VA-API colorspace converter",
57         "Filter/Converter/Video",
58         GST_PLUGIN_DESC,
59         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
60
61 /* Default templates */
62 static const char gst_vaapiconvert_yuv_caps_str[] =
63     "video/x-raw-yuv, "
64     "width  = (int) [ 1, MAX ], "
65     "height = (int) [ 1, MAX ]; ";
66
67 static const char gst_vaapiconvert_vaapi_caps_str[] =
68     GST_VAAPI_SURFACE_CAPS;
69
70 static GstStaticPadTemplate gst_vaapiconvert_sink_factory =
71     GST_STATIC_PAD_TEMPLATE(
72         "sink",
73         GST_PAD_SINK,
74         GST_PAD_ALWAYS,
75         GST_STATIC_CAPS(gst_vaapiconvert_yuv_caps_str));
76
77 static GstStaticPadTemplate gst_vaapiconvert_src_factory =
78     GST_STATIC_PAD_TEMPLATE(
79         "src",
80         GST_PAD_SRC,
81         GST_PAD_ALWAYS,
82         GST_STATIC_CAPS(gst_vaapiconvert_vaapi_caps_str));
83
84 #define GstVideoContextClass GstVideoContextInterface
85 GST_BOILERPLATE_WITH_INTERFACE(
86     GstVaapiConvert,
87     gst_vaapiconvert,
88     GstBaseTransform,
89     GST_TYPE_BASE_TRANSFORM,
90     GstVideoContext,
91     GST_TYPE_VIDEO_CONTEXT,
92     gst_video_context);
93
94 /*
95  * Direct rendering levels (direct-rendering)
96  * 0: upstream allocated YUV pixels
97  * 1: vaapiconvert allocated YUV pixels (mapped from VA image)
98  * 2: vaapiconvert allocated YUV pixels (mapped from VA surface)
99  */
100 #define DIRECT_RENDERING_DEFAULT 2
101
102 enum {
103     PROP_0,
104
105     PROP_DIRECT_RENDERING,
106 };
107
108 static gboolean
109 gst_vaapiconvert_start(GstBaseTransform *trans);
110
111 static gboolean
112 gst_vaapiconvert_stop(GstBaseTransform *trans);
113
114 static GstFlowReturn
115 gst_vaapiconvert_transform(
116     GstBaseTransform *trans,
117     GstBuffer        *inbuf,
118     GstBuffer        *outbuf
119 );
120
121 static GstCaps *
122 gst_vaapiconvert_transform_caps(
123     GstBaseTransform *trans,
124     GstPadDirection   direction,
125     GstCaps          *caps
126 );
127
128 static gboolean
129 gst_vaapiconvert_set_caps(
130     GstBaseTransform *trans,
131     GstCaps          *incaps,
132     GstCaps          *outcaps
133 );
134
135 static gboolean
136 gst_vaapiconvert_get_unit_size(
137     GstBaseTransform *trans,
138     GstCaps          *caps,
139     guint            *size
140 );
141
142 static GstFlowReturn
143 gst_vaapiconvert_sinkpad_buffer_alloc(
144     GstPad           *pad,
145     guint64           offset,
146     guint             size,
147     GstCaps          *caps,
148     GstBuffer       **pbuf
149 );
150
151 static GstFlowReturn
152 gst_vaapiconvert_prepare_output_buffer(
153     GstBaseTransform *trans,
154     GstBuffer        *inbuf,
155     gint              size,
156     GstCaps          *caps,
157     GstBuffer       **poutbuf
158 );
159
160 static gboolean
161 gst_vaapiconvert_query(
162     GstPad   *pad,
163     GstQuery *query
164 );
165
166 /* GstVideoContext interface */
167
168 static void
169 gst_vaapiconvert_set_video_context(GstVideoContext *context, const gchar *type,
170     const GValue *value)
171 {
172   GstVaapiConvert *convert = GST_VAAPICONVERT (context);
173   gst_vaapi_set_display (type, value, &convert->display);
174 }
175
176 static gboolean
177 gst_video_context_supported (GstVaapiConvert *convert, GType iface_type)
178 {
179   return (iface_type == GST_TYPE_VIDEO_CONTEXT);
180 }
181
182 static void
183 gst_video_context_interface_init(GstVideoContextInterface *iface)
184 {
185     iface->set_context = gst_vaapiconvert_set_video_context;
186 }
187
188 static void
189 gst_vaapiconvert_destroy(GstVaapiConvert *convert)
190 {
191     if (convert->images) {
192         g_object_unref(convert->images);
193         convert->images = NULL;
194     }
195
196     if (convert->surfaces) {
197         g_object_unref(convert->surfaces);
198         convert->surfaces = NULL;
199     }
200
201     if (convert->display) {
202         g_object_unref(convert->display);
203         convert->display = NULL;
204     }
205 }
206
207 static void
208 gst_vaapiconvert_base_init(gpointer klass)
209 {
210     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
211
212     gst_element_class_set_details(element_class, &gst_vaapiconvert_details);
213
214     /* sink pad */
215     gst_element_class_add_pad_template(
216         element_class,
217         gst_static_pad_template_get(&gst_vaapiconvert_sink_factory)
218     );
219
220     /* src pad */
221     gst_element_class_add_pad_template(
222         element_class,
223         gst_static_pad_template_get(&gst_vaapiconvert_src_factory)
224     );
225 }
226
227 static void
228 gst_vaapiconvert_finalize(GObject *object)
229 {
230     gst_vaapiconvert_destroy(GST_VAAPICONVERT(object));
231
232     G_OBJECT_CLASS(parent_class)->finalize(object);
233 }
234
235
236 static void
237 gst_vaapiconvert_set_property(
238     GObject      *object,
239     guint         prop_id,
240     const GValue *value,
241     GParamSpec   *pspec
242 )
243 {
244     GstVaapiConvert * const convert = GST_VAAPICONVERT(object);
245
246     switch (prop_id) {
247     case PROP_DIRECT_RENDERING:
248         GST_OBJECT_LOCK(convert);
249         convert->direct_rendering = g_value_get_uint(value);
250         GST_OBJECT_UNLOCK(convert);
251         break;
252     default:
253         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
254         break;
255     }
256 }
257
258 static void
259 gst_vaapiconvert_get_property(
260     GObject    *object,
261     guint       prop_id,
262     GValue     *value,
263     GParamSpec *pspec
264 )
265 {
266     GstVaapiConvert * const convert = GST_VAAPICONVERT(object);
267
268     switch (prop_id) {
269     case PROP_DIRECT_RENDERING:
270         g_value_set_uint(value, convert->direct_rendering);
271         break;
272     default:
273         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
274         break;
275     }
276 }
277
278 static void
279 gst_vaapiconvert_class_init(GstVaapiConvertClass *klass)
280 {
281     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
282     GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
283
284     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapiconvert,
285                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
286
287     object_class->finalize      = gst_vaapiconvert_finalize;
288     object_class->set_property  = gst_vaapiconvert_set_property;
289     object_class->get_property  = gst_vaapiconvert_get_property;
290
291     trans_class->start          = gst_vaapiconvert_start;
292     trans_class->stop           = gst_vaapiconvert_stop;
293     trans_class->transform      = gst_vaapiconvert_transform;
294     trans_class->transform_caps = gst_vaapiconvert_transform_caps;
295     trans_class->set_caps       = gst_vaapiconvert_set_caps;
296     trans_class->get_unit_size  = gst_vaapiconvert_get_unit_size;
297     trans_class->prepare_output_buffer = gst_vaapiconvert_prepare_output_buffer;
298
299     /**
300      * GstVaapiConvert:direct-rendering:
301      *
302      * Selects the direct rendering level.
303      * <orderedlist>
304      * <listitem override="0">
305      *   Disables direct rendering.
306      * </listitem>
307      * <listitem>
308      *   Enables direct rendering to the output buffer. i.e. this
309      *   tries to use a single buffer for both sink and src pads.
310      * </listitem>
311      * <listitem>
312      *   Enables direct rendering to the underlying surface. i.e. with
313      *   drivers supporting vaDeriveImage(), the output surface pixels
314      *   will be modified directly.
315      * </listitem>
316      * </orderedlist>
317      */
318     g_object_class_install_property
319         (object_class,
320          PROP_DIRECT_RENDERING,
321          g_param_spec_uint("direct-rendering",
322                            "Direct rendering",
323                            "Direct rendering level",
324                            0, 2,
325                            DIRECT_RENDERING_DEFAULT,
326                            G_PARAM_READWRITE));
327 }
328
329 static void
330 gst_vaapiconvert_init(GstVaapiConvert *convert, GstVaapiConvertClass *klass)
331 {
332     GstPad *sinkpad, *srcpad;
333
334     convert->display                    = NULL;
335     convert->images                     = NULL;
336     convert->images_reset               = FALSE;
337     convert->image_width                = 0;
338     convert->image_height               = 0;
339     convert->surfaces                   = NULL;
340     convert->surfaces_reset             = FALSE;
341     convert->surface_width              = 0;
342     convert->surface_height             = 0;
343     convert->direct_rendering_caps      = 0;
344     convert->direct_rendering           = G_MAXUINT32;
345
346     /* Override buffer allocator on sink pad */
347     sinkpad = gst_element_get_static_pad(GST_ELEMENT(convert), "sink");
348     gst_pad_set_bufferalloc_function(
349         sinkpad,
350         gst_vaapiconvert_sinkpad_buffer_alloc
351     );
352     g_object_unref(sinkpad);
353
354     /* Override query on src pad */
355     srcpad = gst_element_get_static_pad(GST_ELEMENT(convert), "src");
356     gst_pad_set_query_function(srcpad, gst_vaapiconvert_query);
357 }
358
359 static gboolean
360 gst_vaapiconvert_start(GstBaseTransform *trans)
361 {
362     GstVaapiConvert * const convert = GST_VAAPICONVERT(trans);
363
364     if (!gst_vaapi_ensure_display(convert, &convert->display))
365         return FALSE;
366
367     return TRUE;
368 }
369
370 static gboolean
371 gst_vaapiconvert_stop(GstBaseTransform *trans)
372 {
373     GstVaapiConvert * const convert = GST_VAAPICONVERT(trans);
374
375     if (convert->display) {
376         g_object_unref(convert->display);
377         convert->display = NULL;
378     }
379     return TRUE;
380 }
381
382 static GstFlowReturn
383 gst_vaapiconvert_transform(
384     GstBaseTransform *trans,
385     GstBuffer        *inbuf,
386     GstBuffer        *outbuf
387 )
388 {
389     GstVaapiConvert * const convert = GST_VAAPICONVERT(trans);
390     GstVaapiVideoBuffer *vbuffer;
391     GstVaapiSurface *surface;
392     GstVaapiImage *image;
393     gboolean success;
394
395     vbuffer = GST_VAAPI_VIDEO_BUFFER(outbuf);
396     surface = gst_vaapi_video_buffer_get_surface(vbuffer);
397     if (!surface)
398         return GST_FLOW_UNEXPECTED;
399
400     if (convert->direct_rendering) {
401         if (!GST_VAAPI_IS_VIDEO_BUFFER(inbuf)) {
402             GST_DEBUG("GstVaapiVideoBuffer was expected");
403             return GST_FLOW_UNEXPECTED;
404         }
405
406         vbuffer = GST_VAAPI_VIDEO_BUFFER(inbuf);
407         image   = gst_vaapi_video_buffer_get_image(vbuffer);
408         if (!image)
409             return GST_FLOW_UNEXPECTED;
410         if (!gst_vaapi_image_unmap(image))
411             return GST_FLOW_UNEXPECTED;
412
413         if (convert->direct_rendering < 2) {
414             if (!gst_vaapi_surface_put_image(surface, image))
415                 goto error_put_image;
416         }
417         return GST_FLOW_OK;
418     }
419
420     image = gst_vaapi_video_pool_get_object(convert->images);
421     if (!image)
422         return GST_FLOW_UNEXPECTED;
423
424     gst_vaapi_image_update_from_buffer(image, inbuf);
425     success = gst_vaapi_surface_put_image(surface, image);
426     gst_vaapi_video_pool_put_object(convert->images, image);
427     if (!success)
428         goto error_put_image;
429     return GST_FLOW_OK;
430
431 error_put_image:
432     {
433         GST_WARNING("failed to upload %" GST_FOURCC_FORMAT " image "
434                     "to surface 0x%08x",
435                     GST_FOURCC_ARGS(gst_vaapi_image_get_format(image)),
436                     gst_vaapi_surface_get_id(surface));
437         return GST_FLOW_OK;
438     }
439 }
440
441 static GstCaps *
442 gst_vaapiconvert_transform_caps(
443     GstBaseTransform *trans,
444     GstPadDirection   direction,
445     GstCaps          *caps
446 )
447 {
448     GstVaapiConvert * const convert = GST_VAAPICONVERT(trans);
449     GstCaps *out_caps = NULL;
450     GstStructure *structure;
451     const GValue *v_width, *v_height, *v_framerate, *v_par;
452
453     g_return_val_if_fail(GST_IS_CAPS(caps), NULL);
454
455     structure   = gst_caps_get_structure(caps, 0);
456     v_width     = gst_structure_get_value(structure, "width");
457     v_height    = gst_structure_get_value(structure, "height");
458     v_framerate = gst_structure_get_value(structure, "framerate");
459     v_par       = gst_structure_get_value(structure, "pixel-aspect-ratio");
460
461     if (!v_width || !v_height)
462         return NULL;
463
464     if (direction == GST_PAD_SINK) {
465         if (!gst_structure_has_name(structure, "video/x-raw-yuv"))
466             return NULL;
467         out_caps = gst_caps_from_string(gst_vaapiconvert_vaapi_caps_str);
468     }
469     else {
470         if (!gst_structure_has_name(structure, GST_VAAPI_SURFACE_CAPS_NAME))
471             return NULL;
472         out_caps = gst_caps_from_string(gst_vaapiconvert_yuv_caps_str);
473         if (convert->display) {
474             GstCaps *allowed_caps, *inter_caps;
475             allowed_caps = gst_vaapi_display_get_image_caps(convert->display);
476             if (!allowed_caps)
477                 return NULL;
478             inter_caps = gst_caps_intersect(out_caps, allowed_caps);
479             gst_caps_unref(allowed_caps);
480             gst_caps_unref(out_caps);
481             out_caps = inter_caps;
482         }
483     }
484
485     structure = gst_caps_get_structure(out_caps, 0);
486     gst_structure_set_value(structure, "width", v_width);
487     gst_structure_set_value(structure, "height", v_height);
488     gst_structure_set (structure, "opengl", G_TYPE_BOOLEAN, USE_VAAPI_GLX, NULL);
489     if (v_framerate)
490         gst_structure_set_value(structure, "framerate", v_framerate);
491     if (v_par)
492         gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
493     return out_caps;
494 }
495
496 static gboolean
497 gst_vaapiconvert_ensure_image_pool(GstVaapiConvert *convert, GstCaps *caps)
498 {
499     GstStructure * const structure = gst_caps_get_structure(caps, 0);
500     gint width, height;
501
502     gst_structure_get_int(structure, "width",  &width);
503     gst_structure_get_int(structure, "height", &height);
504
505     if (width != convert->image_width || height != convert->image_height) {
506         convert->image_width  = width;
507         convert->image_height = height;
508         if (convert->images)
509             g_object_unref(convert->images);
510         convert->images = gst_vaapi_image_pool_new(convert->display, caps);
511         if (!convert->images)
512             return FALSE;
513         convert->images_reset = TRUE;
514     }
515     return TRUE;
516 }
517
518 static gboolean
519 gst_vaapiconvert_ensure_surface_pool(GstVaapiConvert *convert, GstCaps *caps)
520 {
521     GstStructure * const structure = gst_caps_get_structure(caps, 0);
522     gint width, height;
523
524     gst_structure_get_int(structure, "width",  &width);
525     gst_structure_get_int(structure, "height", &height);
526
527     if (width != convert->surface_width || height != convert->surface_height) {
528         convert->surface_width  = width;
529         convert->surface_height = height;
530         if (convert->surfaces)
531             g_object_unref(convert->surfaces);
532         convert->surfaces = gst_vaapi_surface_pool_new(convert->display, caps);
533         if (!convert->surfaces)
534             return FALSE;
535         convert->surfaces_reset = TRUE;
536     }
537     return TRUE;
538 }
539
540 static GstVaapiImageFormat
541 gst_video_format_to_vaapi_image_format(GstVideoFormat vformat)
542 {
543     GstVaapiImageFormat vaformat;
544
545     switch (vformat) {
546     case GST_VIDEO_FORMAT_NV12: vaformat = GST_VAAPI_IMAGE_NV12; break;
547     case GST_VIDEO_FORMAT_YV12: vaformat = GST_VAAPI_IMAGE_YV12; break;
548     case GST_VIDEO_FORMAT_I420: vaformat = GST_VAAPI_IMAGE_I420; break;
549     case GST_VIDEO_FORMAT_AYUV: vaformat = GST_VAAPI_IMAGE_AYUV; break;
550     case GST_VIDEO_FORMAT_ARGB: vaformat = GST_VAAPI_IMAGE_ARGB; break;
551     case GST_VIDEO_FORMAT_RGBA: vaformat = GST_VAAPI_IMAGE_RGBA; break;
552     case GST_VIDEO_FORMAT_ABGR: vaformat = GST_VAAPI_IMAGE_ABGR; break;
553     case GST_VIDEO_FORMAT_BGRA: vaformat = GST_VAAPI_IMAGE_BGRA; break;
554     default:                    vaformat = (GstVaapiImageFormat)0; break;
555     }
556     return vaformat;
557 }
558
559 static void
560 gst_vaapiconvert_ensure_direct_rendering_caps(
561     GstVaapiConvert *convert,
562     GstCaps         *caps
563 )
564 {
565     GstVaapiSurface *surface;
566     GstVaapiImage *image;
567     GstVaapiImageFormat vaformat;
568     GstVideoFormat vformat;
569     GstStructure *structure;
570     gint width, height;
571
572     if (!convert->images_reset && !convert->surfaces_reset)
573         return;
574
575     convert->images_reset          = FALSE;
576     convert->surfaces_reset        = FALSE;
577     convert->direct_rendering_caps = 0;
578
579     structure = gst_caps_get_structure(caps, 0);
580     if (!structure)
581         return;
582     gst_structure_get_int(structure, "width",  &width);
583     gst_structure_get_int(structure, "height", &height);
584
585     /* Translate from Gst video format to VA image format */
586     if (!gst_video_format_parse_caps(caps, &vformat, NULL, NULL))
587         return;
588     if (!gst_video_format_is_yuv(vformat))
589         return;
590     vaformat = gst_video_format_to_vaapi_image_format(vformat);
591     if (!vaformat)
592         return;
593
594     /* Check if we can alias sink & output buffers (same data_size) */
595     image = gst_vaapi_video_pool_get_object(convert->images);
596     if (image) {
597         if (convert->direct_rendering_caps == 0 &&
598             (gst_vaapi_image_get_format(image) == vaformat &&
599              gst_vaapi_image_is_linear(image) &&
600              (gst_vaapi_image_get_data_size(image) ==
601               gst_video_format_get_size(vformat, width, height))))
602             convert->direct_rendering_caps = 1;
603         gst_vaapi_video_pool_put_object(convert->images, image);
604     }
605
606     /* Check if we can access to the surface pixels directly */
607     surface = gst_vaapi_video_pool_get_object(convert->surfaces);
608     if (surface) {
609         image = gst_vaapi_surface_derive_image(surface);
610         if (image) {
611             if (gst_vaapi_image_map(image)) {
612                 if (convert->direct_rendering_caps == 1 &&
613                     (gst_vaapi_image_get_format(image) == vaformat &&
614                      gst_vaapi_image_is_linear(image) &&
615                      (gst_vaapi_image_get_data_size(image) ==
616                       gst_video_format_get_size(vformat, width, height))))
617                     convert->direct_rendering_caps = 2;
618                 gst_vaapi_image_unmap(image);
619             }
620             g_object_unref(image);
621         }
622         gst_vaapi_video_pool_put_object(convert->surfaces, surface);
623     }
624 }
625
626 static gboolean
627 gst_vaapiconvert_negotiate_buffers(
628     GstVaapiConvert  *convert,
629     GstCaps          *incaps,
630     GstCaps          *outcaps
631 )
632 {
633     guint dr;
634
635     if (!gst_vaapiconvert_ensure_image_pool(convert, incaps))
636         return FALSE;
637
638     if (!gst_vaapiconvert_ensure_surface_pool(convert, outcaps))
639         return FALSE;
640
641     gst_vaapiconvert_ensure_direct_rendering_caps(convert, incaps);
642     dr = MIN(convert->direct_rendering, convert->direct_rendering_caps);
643     if (convert->direct_rendering != dr) {
644         convert->direct_rendering = dr;
645         GST_DEBUG("direct-rendering level: %d", dr);
646     }
647     return TRUE;
648 }
649
650 static gboolean
651 gst_vaapiconvert_set_caps(
652     GstBaseTransform *trans,
653     GstCaps          *incaps,
654     GstCaps          *outcaps
655 )
656 {
657     GstVaapiConvert * const convert = GST_VAAPICONVERT(trans);
658
659     if (!gst_vaapiconvert_negotiate_buffers(convert, incaps, outcaps))
660         return FALSE;
661
662     return TRUE;
663 }
664
665 static gboolean
666 gst_vaapiconvert_get_unit_size(
667     GstBaseTransform *trans,
668     GstCaps          *caps,
669     guint            *size
670 )
671 {
672     GstStructure * const structure = gst_caps_get_structure(caps, 0);
673     GstVideoFormat format;
674     gint width, height;
675
676     if (gst_structure_has_name(structure, GST_VAAPI_SURFACE_CAPS_NAME))
677         *size = 0;
678     else {
679         if (!gst_video_format_parse_caps(caps, &format, &width, &height))
680             return FALSE;
681         *size = gst_video_format_get_size(format, width, height);
682     }
683     return TRUE;
684 }
685
686 static GstFlowReturn
687 gst_vaapiconvert_buffer_alloc(
688     GstBaseTransform *trans,
689     guint             size,
690     GstCaps          *caps,
691     GstBuffer       **pbuf
692 )
693 {
694     GstVaapiConvert * const convert = GST_VAAPICONVERT(trans);
695     GstBuffer *buffer = NULL;
696     GstVaapiImage *image = NULL;
697     GstVaapiSurface *surface = NULL;
698     GstVaapiVideoBuffer *vbuffer;
699
700     /* Check if we can use direct-rendering */
701     if (!gst_vaapiconvert_negotiate_buffers(convert, caps, caps))
702         goto error;
703     if (!convert->direct_rendering)
704         return GST_FLOW_OK;
705
706     switch (convert->direct_rendering) {
707     case 2:
708         buffer  = gst_vaapi_video_buffer_new_from_pool(convert->surfaces);
709         if (!buffer)
710             goto error;
711         vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
712
713         surface = gst_vaapi_video_buffer_get_surface(vbuffer);
714         image   = gst_vaapi_surface_derive_image(surface);
715         if (image && gst_vaapi_image_get_data_size(image) == size) {
716             gst_vaapi_video_buffer_set_image(vbuffer, image);
717             gst_object_unref(image); /* video buffer owns an extra reference */
718             break;
719         }
720
721         /* We can't use the derive-image optimization. Disable it. */
722         convert->direct_rendering = 1;
723         gst_buffer_unref(buffer);
724         buffer = NULL;
725
726     case 1:
727         buffer  = gst_vaapi_video_buffer_new_from_pool(convert->images);
728         if (!buffer)
729             goto error;
730         vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
731
732         image   = gst_vaapi_video_buffer_get_image(vbuffer);
733         break;
734     }
735     g_assert(image);
736
737     if (!gst_vaapi_image_map(image))
738         goto error;
739
740     GST_BUFFER_DATA(buffer) = gst_vaapi_image_get_plane(image, 0);
741     GST_BUFFER_SIZE(buffer) = gst_vaapi_image_get_data_size(image);
742
743     gst_buffer_set_caps(buffer, caps);
744     *pbuf = buffer;
745     return GST_FLOW_OK;
746
747 error:
748     /* We can't use the inout-buffers optimization. Disable it. */
749     GST_DEBUG("disable in/out buffer optimization");
750     if (buffer)
751         gst_buffer_unref(buffer);
752     convert->direct_rendering = 0;
753     return GST_FLOW_OK;
754 }
755
756 static GstFlowReturn
757 gst_vaapiconvert_sinkpad_buffer_alloc(
758     GstPad           *pad,
759     guint64           offset,
760     guint             size,
761     GstCaps          *caps,
762     GstBuffer       **pbuf
763 )
764 {
765     GstBaseTransform *trans;
766     GstFlowReturn ret;
767
768     trans = GST_BASE_TRANSFORM(gst_pad_get_parent_element(pad));
769     if (!trans)
770         return GST_FLOW_UNEXPECTED;
771
772     ret = gst_vaapiconvert_buffer_alloc(trans, size, caps, pbuf);
773     g_object_unref(trans);
774     return ret;
775 }
776
777 static GstFlowReturn
778 gst_vaapiconvert_prepare_output_buffer(
779     GstBaseTransform *trans,
780     GstBuffer        *inbuf,
781     gint              size,
782     GstCaps          *caps,
783     GstBuffer       **poutbuf
784 )
785 {
786     GstVaapiConvert * const convert = GST_VAAPICONVERT(trans);
787     GstBuffer *buffer = NULL;
788
789     if (convert->direct_rendering == 2) {
790         if (GST_VAAPI_IS_VIDEO_BUFFER(inbuf)) {
791             buffer = gst_vaapi_video_buffer_new_from_buffer(inbuf);
792             GST_BUFFER_SIZE(buffer) = size;
793         }
794         else {
795             GST_DEBUG("upstream element destroyed our in/out buffer");
796             convert->direct_rendering = 1;
797         }
798     }
799
800     if (!buffer) {
801         buffer = gst_vaapi_video_buffer_new_from_pool(convert->surfaces);
802         if (!buffer)
803             return GST_FLOW_UNEXPECTED;
804     }
805
806     gst_buffer_set_caps(buffer, caps);
807     *poutbuf = buffer;
808     return GST_FLOW_OK;
809 }
810
811 static gboolean
812 gst_vaapiconvert_query(GstPad *pad, GstQuery *query)
813 {
814   GstVaapiConvert *convert = GST_VAAPICONVERT (gst_pad_get_parent_element (pad));
815   gboolean res;
816
817   GST_DEBUG ("sharing display %p", convert->display);
818
819   if (gst_vaapi_reply_to_query (query, convert->display))
820     res = TRUE;
821   else
822     res = gst_pad_query_default (pad, query);
823
824   g_object_unref (convert);
825   return res;
826 }