plugins: add support for GstImplementsInterface.
[vaapi:sree-gstreamer-vaapi.git] / gst / vaapi / gstvaapidownload.c
1 /*
2  *  gstvaapidownload.c - VA-API video downloader
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-2012 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapidownload
25  * @short_description: A VA to video flow filter
26  *
27  * vaapidownload converts from VA surfaces to raw YUV pixels.
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(pool) \
41     gst_vaapi_video_buffer_glx_new_from_pool(pool)
42 #define gst_vaapi_video_buffer_new_from_buffer(buffer) \
43     gst_vaapi_video_buffer_glx_new_from_buffer(buffer)
44 #endif
45
46 #include "gstvaapipluginutil.h"
47 #include "gstvaapidownload.h"
48
49 #define GST_PLUGIN_NAME "vaapidownload"
50 #define GST_PLUGIN_DESC "A VA to video flow filter"
51
52 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapidownload);
53 #define GST_CAT_DEFAULT gst_debug_vaapidownload
54
55 /* ElementFactory information */
56 static const GstElementDetails gst_vaapidownload_details =
57     GST_ELEMENT_DETAILS(
58         "VA-API colorspace converter",
59         "Filter/Converter/Video",
60         GST_PLUGIN_DESC,
61         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
62
63 /* Default templates */
64 static const char gst_vaapidownload_yuv_caps_str[] =
65     "video/x-raw-yuv, "
66     "width  = (int) [ 1, MAX ], "
67     "height = (int) [ 1, MAX ]; ";
68
69 static const char gst_vaapidownload_vaapi_caps_str[] =
70     GST_VAAPI_SURFACE_CAPS;
71
72 static GstStaticPadTemplate gst_vaapidownload_sink_factory =
73     GST_STATIC_PAD_TEMPLATE(
74         "sink",
75         GST_PAD_SINK,
76         GST_PAD_ALWAYS,
77         GST_STATIC_CAPS(gst_vaapidownload_vaapi_caps_str));
78
79 static GstStaticPadTemplate gst_vaapidownload_src_factory =
80     GST_STATIC_PAD_TEMPLATE(
81         "src",
82         GST_PAD_SRC,
83         GST_PAD_ALWAYS,
84         GST_STATIC_CAPS(gst_vaapidownload_yuv_caps_str));
85
86 typedef struct _TransformSizeCache TransformSizeCache;
87 struct _TransformSizeCache {
88     GstCaps            *caps;
89     guint               size;
90 };
91
92 struct _GstVaapiDownload {
93     /*< private >*/
94     GstBaseTransform    parent_instance;
95
96     GstVaapiDisplay    *display;
97     GstCaps            *allowed_caps;
98     TransformSizeCache  transform_size_cache[2];
99     GstVaapiVideoPool  *images;
100     GstVaapiImageFormat image_format;
101     guint               image_width;
102     guint               image_height;
103     unsigned int        images_reset    : 1;
104 };
105
106 struct _GstVaapiDownloadClass {
107     /*< private >*/
108     GstBaseTransformClass parent_class;
109 };
110
111 static void
112 gst_vaapidownload_implements_iface_init(GstImplementsInterfaceClass *iface);
113
114 static void
115 gst_video_context_interface_init(GstVideoContextInterface *iface);
116
117 #define GstVideoContextClass GstVideoContextInterface
118 G_DEFINE_TYPE_WITH_CODE(
119     GstVaapiDownload,
120     gst_vaapidownload,
121     GST_TYPE_BASE_TRANSFORM,
122     G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
123                           gst_vaapidownload_implements_iface_init);
124     G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
125                           gst_video_context_interface_init));
126
127 static gboolean
128 gst_vaapidownload_start(GstBaseTransform *trans);
129
130 static gboolean
131 gst_vaapidownload_stop(GstBaseTransform *trans);
132
133 static void
134 gst_vaapidownload_before_transform(GstBaseTransform *trans, GstBuffer *buffer);
135
136 static GstFlowReturn
137 gst_vaapidownload_transform(
138     GstBaseTransform *trans,
139     GstBuffer        *inbuf,
140     GstBuffer        *outbuf
141 );
142
143 static GstCaps *
144 gst_vaapidownload_transform_caps(
145     GstBaseTransform *trans,
146     GstPadDirection   direction,
147     GstCaps          *caps
148 );
149
150 static gboolean
151 gst_vaapidownload_transform_size(
152     GstBaseTransform *trans,
153     GstPadDirection   direction,
154     GstCaps          *caps,
155     guint             size,
156     GstCaps          *othercaps,
157     guint            *othersize
158 );
159
160 static gboolean
161 gst_vaapidownload_set_caps(
162     GstBaseTransform *trans,
163     GstCaps          *incaps,
164     GstCaps          *outcaps
165 );
166
167 static gboolean
168 gst_vaapidownload_query(
169     GstPad   *pad,
170     GstQuery *query
171 );
172
173 /* GstImplementsInterface interface */
174
175 static gboolean
176 gst_vaapidownload_implements_interface_supported(
177     GstImplementsInterface *iface,
178     GType                   type
179 )
180 {
181     return (type == GST_TYPE_VIDEO_CONTEXT);
182 }
183
184 static void
185 gst_vaapidownload_implements_iface_init(GstImplementsInterfaceClass *iface)
186 {
187     iface->supported = gst_vaapidownload_implements_interface_supported;
188 }
189
190 /* GstVideoContext interface */
191
192 static void
193 gst_vaapidownload_set_video_context(GstVideoContext *context, const gchar *type,
194     const GValue *value)
195 {
196   GstVaapiDownload *download = GST_VAAPIDOWNLOAD (context);
197   gst_vaapi_set_display (type, value, &download->display);
198 }
199
200 static void
201 gst_video_context_interface_init(GstVideoContextInterface *iface)
202 {
203     iface->set_context = gst_vaapidownload_set_video_context;
204 }
205
206 static void
207 gst_vaapidownload_destroy(GstVaapiDownload *download)
208 {
209     guint i;
210
211     for (i = 0; i < G_N_ELEMENTS(download->transform_size_cache); i++) {
212         TransformSizeCache * const tsc = &download->transform_size_cache[i];
213         if (tsc->caps) {
214             gst_caps_unref(tsc->caps);
215             tsc->caps = NULL;
216             tsc->size = 0;
217         }
218     }
219
220     if (download->allowed_caps) {
221         gst_caps_unref(download->allowed_caps);
222         download->allowed_caps = NULL;
223     }
224
225     if (download->images) {
226         g_object_unref(download->images);
227         download->images = NULL;
228     }
229
230     if (download->display) {
231         g_object_unref(download->display);
232         download->display = NULL;
233     }
234 }
235
236 static void
237 gst_vaapidownload_finalize(GObject *object)
238 {
239     gst_vaapidownload_destroy(GST_VAAPIDOWNLOAD(object));
240
241     G_OBJECT_CLASS(gst_vaapidownload_parent_class)->finalize(object);
242 }
243
244 static void
245 gst_vaapidownload_class_init(GstVaapiDownloadClass *klass)
246 {
247     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
248     GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
249     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
250     GstPadTemplate *pad_template;
251
252     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapidownload,
253                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
254
255     object_class->finalize        = gst_vaapidownload_finalize;
256     trans_class->start            = gst_vaapidownload_start;
257     trans_class->stop             = gst_vaapidownload_stop;
258     trans_class->before_transform = gst_vaapidownload_before_transform;
259     trans_class->transform        = gst_vaapidownload_transform;
260     trans_class->transform_caps   = gst_vaapidownload_transform_caps;
261     trans_class->transform_size   = gst_vaapidownload_transform_size;
262     trans_class->set_caps         = gst_vaapidownload_set_caps;
263
264     gst_element_class_set_details_simple(
265         element_class,
266         gst_vaapidownload_details.longname,
267         gst_vaapidownload_details.klass,
268         gst_vaapidownload_details.description,
269         gst_vaapidownload_details.author
270     );
271
272     /* sink pad */
273     pad_template = gst_static_pad_template_get(&gst_vaapidownload_sink_factory);
274     gst_element_class_add_pad_template(element_class, pad_template);
275     gst_object_unref(pad_template);
276
277     /* src pad */
278     pad_template = gst_static_pad_template_get(&gst_vaapidownload_src_factory);
279     gst_element_class_add_pad_template(element_class, pad_template);
280     gst_object_unref(pad_template);
281 }
282
283 static void
284 gst_vaapidownload_init(GstVaapiDownload *download)
285 {
286     GstPad *sinkpad, *srcpad;
287
288     download->display           = NULL;
289     download->allowed_caps      = NULL;
290     download->images            = NULL;
291     download->images_reset      = FALSE;
292     download->image_format      = (GstVaapiImageFormat)0;
293     download->image_width       = 0;
294     download->image_height      = 0;
295
296     /* Override buffer allocator on sink pad */
297     sinkpad = gst_element_get_static_pad(GST_ELEMENT(download), "sink");
298     gst_pad_set_query_function(sinkpad, gst_vaapidownload_query);
299     gst_object_unref(sinkpad);
300
301     /* Override query on src pad */
302     srcpad = gst_element_get_static_pad(GST_ELEMENT(download), "src");
303     gst_pad_set_query_function(srcpad, gst_vaapidownload_query);
304     gst_object_unref(srcpad);
305 }
306
307 static gboolean
308 gst_vaapidownload_start(GstBaseTransform *trans)
309 {
310     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(trans);
311
312     if (!gst_vaapi_ensure_display(download, &download->display))
313         return FALSE;
314     return TRUE;
315 }
316
317 static gboolean
318 gst_vaapidownload_stop(GstBaseTransform *trans)
319 {
320     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(trans);
321
322     if (download->display) {
323         g_object_unref(download->display);
324         download->display = NULL;
325     }
326     return TRUE;
327 }
328
329 static GstVaapiImageFormat
330 get_surface_format(GstVaapiSurface *surface)
331 {
332     GstVaapiImage *image;
333     GstVaapiImageFormat format = GST_VAAPI_IMAGE_NV12;
334
335     /* XXX: NV12 is assumed by default */
336     image = gst_vaapi_surface_derive_image(surface);
337     if (image) {
338         format = gst_vaapi_image_get_format(image);
339         g_object_unref(image);
340     }
341     return format;
342 }
343
344 static gboolean
345 gst_vaapidownload_update_src_caps(GstVaapiDownload *download, GstBuffer *buffer)
346 {
347     GstVaapiVideoBuffer *vbuffer;
348     GstVaapiSurface *surface;
349     GstVaapiImageFormat format;
350     GstPad *srcpad;
351     GstCaps *in_caps, *out_caps;
352
353     vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
354     surface = gst_vaapi_video_buffer_get_surface(vbuffer);
355     if (!surface) {
356         GST_WARNING("failed to retrieve VA surface from buffer");
357         return FALSE;
358     }
359
360     format = get_surface_format(surface);
361     if (format == download->image_format)
362         return TRUE;
363
364     in_caps = GST_BUFFER_CAPS(buffer);
365     if (!in_caps) {
366         GST_WARNING("failed to retrieve caps from buffer");
367         return FALSE;
368     }
369
370     out_caps = gst_vaapi_image_format_get_caps(format);
371     if (!out_caps) {
372         GST_WARNING("failed to create caps from format %" GST_FOURCC_FORMAT,
373                     GST_FOURCC_ARGS(format));
374         return FALSE;
375     }
376
377     if (!gst_vaapi_append_surface_caps(out_caps, in_caps)) {
378         gst_caps_unref(out_caps);
379         return FALSE;
380     }
381
382     /* Try to renegotiate downstream caps */
383     srcpad = gst_element_get_static_pad(GST_ELEMENT(download), "src");
384     gst_pad_set_caps(srcpad, out_caps);
385     gst_object_unref(srcpad);
386
387     gst_vaapidownload_set_caps(GST_BASE_TRANSFORM(download), in_caps, out_caps);
388     gst_caps_replace(&download->allowed_caps, out_caps);
389     gst_caps_unref(out_caps);
390     return TRUE;
391 }
392
393 static void
394 gst_vaapidownload_before_transform(GstBaseTransform *trans, GstBuffer *buffer)
395 {
396     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(trans);
397
398     gst_vaapidownload_update_src_caps(download, buffer);
399 }
400
401 static GstFlowReturn
402 gst_vaapidownload_transform(
403     GstBaseTransform *trans,
404     GstBuffer        *inbuf,
405     GstBuffer        *outbuf
406 )
407 {
408     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(trans);
409     GstVaapiVideoBuffer *vbuffer;
410     GstVaapiSurface *surface;
411     GstVaapiImage *image = NULL;
412     gboolean success;
413
414     vbuffer = GST_VAAPI_VIDEO_BUFFER(inbuf);
415     surface = gst_vaapi_video_buffer_get_surface(vbuffer);
416     if (!surface)
417         return GST_FLOW_UNEXPECTED;
418
419     image = gst_vaapi_video_pool_get_object(download->images);
420     if (!image)
421         return GST_FLOW_UNEXPECTED;
422     if (!gst_vaapi_surface_get_image(surface, image))
423         goto error_get_image;
424
425     success = gst_vaapi_image_get_buffer(image, outbuf, NULL);
426     gst_vaapi_video_pool_put_object(download->images, image);
427     if (!success)
428         goto error_get_buffer;
429     return GST_FLOW_OK;
430
431 error_get_image:
432     {
433         GST_WARNING("failed to download %" GST_FOURCC_FORMAT " image "
434                     "from surface 0x%08x",
435                     GST_FOURCC_ARGS(gst_vaapi_image_get_format(image)),
436                     gst_vaapi_surface_get_id(surface));
437         gst_vaapi_video_pool_put_object(download->images, image);
438         return GST_FLOW_UNEXPECTED;
439     }
440
441 error_get_buffer:
442     {
443         GST_WARNING("failed to transfer image to output video buffer");
444         return GST_FLOW_UNEXPECTED;
445     }
446 }
447
448 static GstCaps *
449 gst_vaapidownload_transform_caps(
450     GstBaseTransform *trans,
451     GstPadDirection   direction,
452     GstCaps          *caps
453 )
454 {
455     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(trans);
456     GstPad *srcpad;
457     GstCaps *allowed_caps, *inter_caps, *out_caps = NULL;
458     GstStructure *structure;
459
460     g_return_val_if_fail(GST_IS_CAPS(caps), NULL);
461
462     structure = gst_caps_get_structure(caps, 0);
463
464     if (direction == GST_PAD_SINK) {
465         if (!gst_structure_has_name(structure, GST_VAAPI_SURFACE_CAPS_NAME))
466             return NULL;
467         if (!gst_vaapi_ensure_display(download, &download->display))
468             return NULL;
469         out_caps = gst_caps_from_string(gst_vaapidownload_yuv_caps_str);
470
471         /* Build up allowed caps */
472         /* XXX: we don't know the decoded surface format yet so we
473            expose whatever VA images we support */
474         if (download->allowed_caps)
475             allowed_caps = gst_caps_ref(download->allowed_caps);
476         else {
477             allowed_caps = gst_vaapi_display_get_image_caps(download->display);
478             if (!allowed_caps)
479                 return NULL;
480         }
481         inter_caps = gst_caps_intersect(out_caps, allowed_caps);
482         gst_caps_unref(allowed_caps);
483         gst_caps_unref(out_caps);
484         out_caps = inter_caps;
485
486         /* Intersect with allowed caps from the peer, if any */
487         srcpad = gst_element_get_static_pad(GST_ELEMENT(download), "src");
488         allowed_caps = gst_pad_peer_get_caps(srcpad);
489         if (allowed_caps) {
490             inter_caps = gst_caps_intersect(out_caps, allowed_caps);
491             gst_caps_unref(allowed_caps);
492             gst_caps_unref(out_caps);
493             out_caps = inter_caps;
494         }
495     }
496     else {
497         if (!gst_structure_has_name(structure, "video/x-raw-yuv"))
498             return NULL;
499         out_caps = gst_caps_from_string(gst_vaapidownload_vaapi_caps_str);
500
501         structure = gst_caps_get_structure(out_caps, 0);
502         gst_structure_set(
503             structure,
504             "type", G_TYPE_STRING, "vaapi",
505             "opengl", G_TYPE_BOOLEAN, USE_VAAPI_GLX,
506             NULL
507         );
508     }
509
510     if (!gst_vaapi_append_surface_caps(out_caps, caps)) {
511         gst_caps_unref(out_caps);
512         return NULL;
513     }
514     return out_caps;
515 }
516
517 static gboolean
518 gst_vaapidownload_ensure_image_pool(GstVaapiDownload *download, GstCaps *caps)
519 {
520     GstStructure * const structure = gst_caps_get_structure(caps, 0);
521     GstVaapiImageFormat format;
522     gint width, height;
523
524     format = gst_vaapi_image_format_from_caps(caps);
525     gst_structure_get_int(structure, "width",  &width);
526     gst_structure_get_int(structure, "height", &height);
527
528     if (format != download->image_format ||
529         width  != download->image_width  ||
530         height != download->image_height) {
531         download->image_format = format;
532         download->image_width  = width;
533         download->image_height = height;
534         if (download->images)
535             g_object_unref(download->images);
536         download->images = gst_vaapi_image_pool_new(download->display, caps);
537         if (!download->images)
538             return FALSE;
539         download->images_reset = TRUE;
540     }
541     return TRUE;
542 }
543
544 static inline gboolean
545 gst_vaapidownload_negotiate_buffers(
546     GstVaapiDownload  *download,
547     GstCaps          *incaps,
548     GstCaps          *outcaps
549 )
550 {
551     if (!gst_vaapidownload_ensure_image_pool(download, outcaps))
552         return FALSE;
553     return TRUE;
554 }
555
556 static gboolean
557 gst_vaapidownload_set_caps(
558     GstBaseTransform *trans,
559     GstCaps          *incaps,
560     GstCaps          *outcaps
561 )
562 {
563     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(trans);
564
565     if (!gst_vaapidownload_negotiate_buffers(download, incaps, outcaps))
566         return FALSE;
567     return TRUE;
568 }
569
570 static gboolean
571 gst_vaapidownload_transform_size(
572     GstBaseTransform *trans,
573     GstPadDirection   direction,
574     GstCaps          *caps,
575     guint             size,
576     GstCaps          *othercaps,
577     guint            *othersize
578 )
579 {
580     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(trans);
581     GstStructure * const structure = gst_caps_get_structure(othercaps, 0);
582     GstVideoFormat format;
583     gint width, height;
584     guint i;
585
586     /* Lookup in cache */
587     for (i = 0; i < G_N_ELEMENTS(download->transform_size_cache); i++) {
588         TransformSizeCache * const tsc = &download->transform_size_cache[i];
589         if (tsc->caps && tsc->caps == othercaps) {
590             *othersize = tsc->size;
591             return TRUE;
592         }
593     }
594
595     /* Compute requested buffer size */
596     if (gst_structure_has_name(structure, GST_VAAPI_SURFACE_CAPS_NAME))
597         *othersize = 0;
598     else {
599         if (!gst_video_format_parse_caps(othercaps, &format, &width, &height))
600             return FALSE;
601         *othersize = gst_video_format_get_size(format, width, height);
602     }
603
604     /* Update cache */
605     for (i = 0; i < G_N_ELEMENTS(download->transform_size_cache); i++) {
606         TransformSizeCache * const tsc = &download->transform_size_cache[i];
607         if (!tsc->caps) {
608             gst_caps_replace(&tsc->caps, othercaps);
609             tsc->size = *othersize;
610         }
611     }
612     return TRUE;
613 }
614
615 static gboolean
616 gst_vaapidownload_query(GstPad *pad, GstQuery *query)
617 {
618     GstVaapiDownload * const download = GST_VAAPIDOWNLOAD(gst_pad_get_parent_element(pad));
619     gboolean res;
620
621     GST_DEBUG("sharing display %p", download->display);
622
623     if (gst_vaapi_reply_to_query(query, download->display))
624         res = TRUE;
625     else
626         res = gst_pad_query_default(pad, query);
627
628     g_object_unref(download);
629     return res;
630
631 }