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