2 * gstvaapiupload.c - VA-API video uploader
4 * Copyright (C) 2010-2011 Splitted-Desktop Systems
5 * Copyright (C) 2011-2012 Intel Corporation
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.
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.
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
24 * SECTION:gstvaapiupload
25 * @short_description: A video to VA flow filter
27 * vaapiupload converts from raw YUV pixels to VA surfaces suitable
28 * for the vaapisink element, for example.
34 #include <gst/video/video.h>
35 #include <gst/video/videocontext.h>
36 #include <gst/vaapi/gstvaapivideosink.h>
37 #include <gst/vaapi/gstvaapivideobuffer.h>
40 #include <gst/vaapi/gstvaapivideobuffer_glx.h>
41 #define gst_vaapi_video_buffer_new_from_pool(pool) \
42 gst_vaapi_video_buffer_glx_new_from_pool(pool)
43 #define gst_vaapi_video_buffer_new_from_buffer(buffer) \
44 gst_vaapi_video_buffer_glx_new_from_buffer(buffer)
47 #include "gstvaapipluginutil.h"
48 #include "gstvaapiupload.h"
50 #define GST_PLUGIN_NAME "vaapiupload"
51 #define GST_PLUGIN_DESC "A video to VA flow filter"
53 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapiupload);
54 #define GST_CAT_DEFAULT gst_debug_vaapiupload
56 /* ElementFactory information */
57 static const GstElementDetails gst_vaapiupload_details =
59 "VA-API colorspace converter",
60 "Filter/Converter/Video",
62 "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
64 /* Default templates */
65 static const char gst_vaapiupload_yuv_caps_str[] =
67 "width = (int) [ 1, MAX ], "
68 "height = (int) [ 1, MAX ]; ";
70 static const char gst_vaapiupload_vaapi_caps_str[] =
71 GST_VAAPI_SURFACE_CAPS;
73 static GstStaticPadTemplate gst_vaapiupload_sink_factory =
74 GST_STATIC_PAD_TEMPLATE(
78 GST_STATIC_CAPS(gst_vaapiupload_yuv_caps_str));
80 static GstStaticPadTemplate gst_vaapiupload_src_factory =
81 GST_STATIC_PAD_TEMPLATE(
85 GST_STATIC_CAPS(gst_vaapiupload_vaapi_caps_str));
88 gst_vaapiupload_implements_iface_init(GstImplementsInterfaceClass *iface);
91 gst_video_context_interface_init(GstVideoContextInterface *iface);
93 #define GstVideoContextClass GstVideoContextInterface
94 G_DEFINE_TYPE_WITH_CODE(
97 GST_TYPE_BASE_TRANSFORM,
98 G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
99 gst_vaapiupload_implements_iface_init);
100 G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
101 gst_video_context_interface_init));
104 * Direct rendering levels (direct-rendering)
105 * 0: upstream allocated YUV pixels
106 * 1: vaapiupload allocated YUV pixels (mapped from VA image)
107 * 2: vaapiupload allocated YUV pixels (mapped from VA surface)
109 #define DIRECT_RENDERING_DEFAULT 2
114 PROP_DIRECT_RENDERING,
118 gst_vaapiupload_start(GstBaseTransform *trans);
121 gst_vaapiupload_stop(GstBaseTransform *trans);
124 gst_vaapiupload_transform(
125 GstBaseTransform *trans,
131 gst_vaapiupload_transform_caps(
132 GstBaseTransform *trans,
133 GstPadDirection direction,
138 gst_vaapiupload_set_caps(
139 GstBaseTransform *trans,
145 gst_vaapiupload_get_unit_size(
146 GstBaseTransform *trans,
152 gst_vaapiupload_sinkpad_buffer_alloc(
161 gst_vaapiupload_prepare_output_buffer(
162 GstBaseTransform *trans,
170 gst_vaapiupload_query(
175 /* GstImplementsInterface interface */
178 gst_vaapiupload_implements_interface_supported(
179 GstImplementsInterface *iface,
183 return (type == GST_TYPE_VIDEO_CONTEXT);
187 gst_vaapiupload_implements_iface_init(GstImplementsInterfaceClass *iface)
189 iface->supported = gst_vaapiupload_implements_interface_supported;
192 /* GstVideoContext interface */
195 gst_vaapiupload_set_video_context(GstVideoContext *context, const gchar *type,
198 GstVaapiUpload *upload = GST_VAAPIUPLOAD (context);
199 gst_vaapi_set_display (type, value, &upload->display);
203 gst_video_context_interface_init(GstVideoContextInterface *iface)
205 iface->set_context = gst_vaapiupload_set_video_context;
209 gst_vaapiupload_destroy(GstVaapiUpload *upload)
211 g_clear_object(&upload->images);
212 g_clear_object(&upload->surfaces);
213 g_clear_object(&upload->display);
217 gst_vaapiupload_finalize(GObject *object)
219 gst_vaapiupload_destroy(GST_VAAPIUPLOAD(object));
221 G_OBJECT_CLASS(gst_vaapiupload_parent_class)->finalize(object);
226 gst_vaapiupload_set_property(
233 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(object);
236 case PROP_DIRECT_RENDERING:
237 GST_OBJECT_LOCK(upload);
238 upload->direct_rendering = g_value_get_uint(value);
239 GST_OBJECT_UNLOCK(upload);
242 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
248 gst_vaapiupload_get_property(
255 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(object);
258 case PROP_DIRECT_RENDERING:
259 g_value_set_uint(value, upload->direct_rendering);
262 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
268 gst_vaapiupload_class_init(GstVaapiUploadClass *klass)
270 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
271 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
272 GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
273 GstPadTemplate *pad_template;
275 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapiupload,
276 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
278 object_class->finalize = gst_vaapiupload_finalize;
279 object_class->set_property = gst_vaapiupload_set_property;
280 object_class->get_property = gst_vaapiupload_get_property;
282 trans_class->start = gst_vaapiupload_start;
283 trans_class->stop = gst_vaapiupload_stop;
284 trans_class->transform = gst_vaapiupload_transform;
285 trans_class->transform_caps = gst_vaapiupload_transform_caps;
286 trans_class->set_caps = gst_vaapiupload_set_caps;
287 trans_class->get_unit_size = gst_vaapiupload_get_unit_size;
288 trans_class->prepare_output_buffer = gst_vaapiupload_prepare_output_buffer;
290 gst_element_class_set_details_simple(
292 gst_vaapiupload_details.longname,
293 gst_vaapiupload_details.klass,
294 gst_vaapiupload_details.description,
295 gst_vaapiupload_details.author
299 pad_template = gst_static_pad_template_get(&gst_vaapiupload_sink_factory);
300 gst_element_class_add_pad_template(element_class, pad_template);
301 gst_object_unref(pad_template);
304 pad_template = gst_static_pad_template_get(&gst_vaapiupload_src_factory);
305 gst_element_class_add_pad_template(element_class, pad_template);
306 gst_object_unref(pad_template);
309 * GstVaapiUpload:direct-rendering:
311 * Selects the direct rendering level.
313 * <listitem override="0">
314 * Disables direct rendering.
317 * Enables direct rendering to the output buffer. i.e. this
318 * tries to use a single buffer for both sink and src pads.
321 * Enables direct rendering to the underlying surface. i.e. with
322 * drivers supporting vaDeriveImage(), the output surface pixels
323 * will be modified directly.
327 g_object_class_install_property
329 PROP_DIRECT_RENDERING,
330 g_param_spec_uint("direct-rendering",
332 "Direct rendering level",
334 DIRECT_RENDERING_DEFAULT,
339 gst_vaapiupload_init(GstVaapiUpload *upload)
341 GstPad *sinkpad, *srcpad;
343 upload->display = NULL;
344 upload->images = NULL;
345 upload->images_reset = FALSE;
346 upload->image_width = 0;
347 upload->image_height = 0;
348 upload->surfaces = NULL;
349 upload->surfaces_reset = FALSE;
350 upload->surface_width = 0;
351 upload->surface_height = 0;
352 upload->direct_rendering_caps = 0;
353 upload->direct_rendering = G_MAXUINT32;
355 /* Override buffer allocator on sink pad */
356 sinkpad = gst_element_get_static_pad(GST_ELEMENT(upload), "sink");
357 gst_pad_set_bufferalloc_function(
359 gst_vaapiupload_sinkpad_buffer_alloc
361 gst_pad_set_query_function(sinkpad, gst_vaapiupload_query);
362 g_object_unref(sinkpad);
364 /* Override query on src pad */
365 srcpad = gst_element_get_static_pad(GST_ELEMENT(upload), "src");
366 gst_pad_set_query_function(srcpad, gst_vaapiupload_query);
367 g_object_unref(srcpad);
371 gst_vaapiupload_start(GstBaseTransform *trans)
373 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
375 if (!gst_vaapi_ensure_display(upload, &upload->display))
382 gst_vaapiupload_stop(GstBaseTransform *trans)
384 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
386 g_clear_object(&upload->display);
392 gst_vaapiupload_transform(
393 GstBaseTransform *trans,
398 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
399 GstVaapiVideoBuffer *vbuffer;
400 GstVaapiSurface *surface;
401 GstVaapiImage *image;
404 vbuffer = GST_VAAPI_VIDEO_BUFFER(outbuf);
405 surface = gst_vaapi_video_buffer_get_surface(vbuffer);
407 return GST_FLOW_UNEXPECTED;
409 if (upload->direct_rendering) {
410 if (!GST_VAAPI_IS_VIDEO_BUFFER(inbuf)) {
411 GST_DEBUG("GstVaapiVideoBuffer was expected");
412 return GST_FLOW_UNEXPECTED;
415 vbuffer = GST_VAAPI_VIDEO_BUFFER(inbuf);
416 image = gst_vaapi_video_buffer_get_image(vbuffer);
418 return GST_FLOW_UNEXPECTED;
419 if (!gst_vaapi_image_unmap(image))
420 return GST_FLOW_UNEXPECTED;
422 if (upload->direct_rendering < 2) {
423 if (!gst_vaapi_surface_put_image(surface, image))
424 goto error_put_image;
429 image = gst_vaapi_video_pool_get_object(upload->images);
431 return GST_FLOW_UNEXPECTED;
433 gst_vaapi_image_update_from_buffer(image, inbuf, NULL);
434 success = gst_vaapi_surface_put_image(surface, image);
435 gst_vaapi_video_pool_put_object(upload->images, image);
437 goto error_put_image;
442 GST_WARNING("failed to upload %" GST_FOURCC_FORMAT " image "
444 GST_FOURCC_ARGS(gst_vaapi_image_get_format(image)),
445 gst_vaapi_surface_get_id(surface));
451 gst_vaapiupload_transform_caps(
452 GstBaseTransform *trans,
453 GstPadDirection direction,
457 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
458 GstCaps *out_caps = NULL;
459 GstStructure *structure;
461 g_return_val_if_fail(GST_IS_CAPS(caps), NULL);
463 structure = gst_caps_get_structure(caps, 0);
465 if (direction == GST_PAD_SINK) {
466 if (!gst_structure_has_name(structure, "video/x-raw-yuv"))
468 out_caps = gst_caps_from_string(gst_vaapiupload_vaapi_caps_str);
470 structure = gst_caps_get_structure(out_caps, 0);
473 "type", G_TYPE_STRING, "vaapi",
474 "opengl", G_TYPE_BOOLEAN, USE_GLX,
479 if (!gst_structure_has_name(structure, GST_VAAPI_SURFACE_CAPS_NAME))
481 out_caps = gst_caps_from_string(gst_vaapiupload_yuv_caps_str);
482 if (upload->display) {
483 GstCaps *allowed_caps, *inter_caps;
484 allowed_caps = gst_vaapi_display_get_image_caps(upload->display);
487 inter_caps = gst_caps_intersect(out_caps, allowed_caps);
488 gst_caps_unref(allowed_caps);
489 gst_caps_unref(out_caps);
490 out_caps = inter_caps;
494 if (!gst_vaapi_append_surface_caps(out_caps, caps)) {
495 gst_caps_unref(out_caps);
502 gst_vaapiupload_ensure_image_pool(GstVaapiUpload *upload, GstCaps *caps)
504 GstStructure * const structure = gst_caps_get_structure(caps, 0);
507 gst_structure_get_int(structure, "width", &width);
508 gst_structure_get_int(structure, "height", &height);
510 if (width != upload->image_width || height != upload->image_height) {
511 upload->image_width = width;
512 upload->image_height = height;
513 g_clear_object(&upload->images);
514 upload->images = gst_vaapi_image_pool_new(upload->display, caps);
517 upload->images_reset = TRUE;
523 gst_vaapiupload_ensure_surface_pool(GstVaapiUpload *upload, GstCaps *caps)
525 GstStructure * const structure = gst_caps_get_structure(caps, 0);
528 gst_structure_get_int(structure, "width", &width);
529 gst_structure_get_int(structure, "height", &height);
531 if (width != upload->surface_width || height != upload->surface_height) {
532 upload->surface_width = width;
533 upload->surface_height = height;
534 g_clear_object(&upload->surfaces);
535 upload->surfaces = gst_vaapi_surface_pool_new(upload->display, caps);
536 if (!upload->surfaces)
538 upload->surfaces_reset = TRUE;
544 gst_vaapiupload_ensure_direct_rendering_caps(
545 GstVaapiUpload *upload,
549 GstVaapiSurface *surface;
550 GstVaapiImage *image;
551 GstVaapiImageFormat vaformat;
552 GstVideoFormat vformat;
553 GstStructure *structure;
556 if (!upload->images_reset && !upload->surfaces_reset)
559 upload->images_reset = FALSE;
560 upload->surfaces_reset = FALSE;
561 upload->direct_rendering_caps = 0;
563 structure = gst_caps_get_structure(caps, 0);
566 gst_structure_get_int(structure, "width", &width);
567 gst_structure_get_int(structure, "height", &height);
569 /* Translate from Gst video format to VA image format */
570 if (!gst_video_format_parse_caps(caps, &vformat, NULL, NULL))
572 if (!gst_video_format_is_yuv(vformat))
574 vaformat = gst_vaapi_image_format_from_video(vformat);
578 /* Check if we can alias sink & output buffers (same data_size) */
579 image = gst_vaapi_video_pool_get_object(upload->images);
581 if (upload->direct_rendering_caps == 0 &&
582 (gst_vaapi_image_get_format(image) == vaformat &&
583 gst_vaapi_image_is_linear(image) &&
584 (gst_vaapi_image_get_data_size(image) ==
585 gst_video_format_get_size(vformat, width, height))))
586 upload->direct_rendering_caps = 1;
587 gst_vaapi_video_pool_put_object(upload->images, image);
590 /* Check if we can access to the surface pixels directly */
591 surface = gst_vaapi_video_pool_get_object(upload->surfaces);
593 image = gst_vaapi_surface_derive_image(surface);
595 if (gst_vaapi_image_map(image)) {
596 if (upload->direct_rendering_caps == 1 &&
597 (gst_vaapi_image_get_format(image) == vaformat &&
598 gst_vaapi_image_is_linear(image) &&
599 (gst_vaapi_image_get_data_size(image) ==
600 gst_video_format_get_size(vformat, width, height))))
601 upload->direct_rendering_caps = 2;
602 gst_vaapi_image_unmap(image);
604 g_object_unref(image);
606 gst_vaapi_video_pool_put_object(upload->surfaces, surface);
611 gst_vaapiupload_negotiate_buffers(
612 GstVaapiUpload *upload,
619 if (!gst_vaapiupload_ensure_image_pool(upload, incaps))
622 if (!gst_vaapiupload_ensure_surface_pool(upload, outcaps))
625 gst_vaapiupload_ensure_direct_rendering_caps(upload, incaps);
626 dr = MIN(upload->direct_rendering, upload->direct_rendering_caps);
627 if (upload->direct_rendering != dr) {
628 upload->direct_rendering = dr;
629 GST_DEBUG("direct-rendering level: %d", dr);
635 gst_vaapiupload_set_caps(
636 GstBaseTransform *trans,
641 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
643 if (!gst_vaapiupload_negotiate_buffers(upload, incaps, outcaps))
650 gst_vaapiupload_get_unit_size(
651 GstBaseTransform *trans,
656 GstStructure * const structure = gst_caps_get_structure(caps, 0);
657 GstVideoFormat format;
660 if (gst_structure_has_name(structure, GST_VAAPI_SURFACE_CAPS_NAME))
663 if (!gst_video_format_parse_caps(caps, &format, &width, &height))
665 *size = gst_video_format_get_size(format, width, height);
671 gst_vaapiupload_buffer_alloc(
672 GstBaseTransform *trans,
678 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
679 GstBuffer *buffer = NULL;
680 GstVaapiImage *image = NULL;
681 GstVaapiSurface *surface = NULL;
682 GstVaapiVideoBuffer *vbuffer;
684 /* Check if we can use direct-rendering */
685 if (!gst_vaapiupload_negotiate_buffers(upload, caps, caps))
687 if (!upload->direct_rendering)
690 switch (upload->direct_rendering) {
692 buffer = gst_vaapi_video_buffer_new_from_pool(upload->surfaces);
695 vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
697 surface = gst_vaapi_video_buffer_get_surface(vbuffer);
698 image = gst_vaapi_surface_derive_image(surface);
699 if (image && gst_vaapi_image_get_data_size(image) == size) {
700 gst_vaapi_video_buffer_set_image(vbuffer, image);
701 g_object_unref(image); /* video buffer owns an extra reference */
705 /* We can't use the derive-image optimization. Disable it. */
706 upload->direct_rendering = 1;
707 gst_buffer_unref(buffer);
711 buffer = gst_vaapi_video_buffer_new_from_pool(upload->images);
714 vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
716 image = gst_vaapi_video_buffer_get_image(vbuffer);
721 if (!gst_vaapi_image_map(image))
724 GST_BUFFER_DATA(buffer) = gst_vaapi_image_get_plane(image, 0);
725 GST_BUFFER_SIZE(buffer) = gst_vaapi_image_get_data_size(image);
727 gst_buffer_set_caps(buffer, caps);
732 /* We can't use the inout-buffers optimization. Disable it. */
733 GST_DEBUG("disable in/out buffer optimization");
735 gst_buffer_unref(buffer);
736 upload->direct_rendering = 0;
741 gst_vaapiupload_sinkpad_buffer_alloc(
749 GstBaseTransform *trans;
752 trans = GST_BASE_TRANSFORM(gst_pad_get_parent_element(pad));
754 return GST_FLOW_UNEXPECTED;
756 ret = gst_vaapiupload_buffer_alloc(trans, size, caps, pbuf);
757 g_object_unref(trans);
762 gst_vaapiupload_prepare_output_buffer(
763 GstBaseTransform *trans,
770 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
771 GstBuffer *buffer = NULL;
773 if (upload->direct_rendering == 2) {
774 if (GST_VAAPI_IS_VIDEO_BUFFER(inbuf)) {
775 buffer = gst_vaapi_video_buffer_new_from_buffer(inbuf);
776 GST_BUFFER_SIZE(buffer) = size;
779 GST_DEBUG("upstream element destroyed our in/out buffer");
780 upload->direct_rendering = 1;
785 buffer = gst_vaapi_video_buffer_new_from_pool(upload->surfaces);
787 return GST_FLOW_UNEXPECTED;
788 gst_buffer_set_caps(buffer, caps);
796 gst_vaapiupload_query(GstPad *pad, GstQuery *query)
798 GstVaapiUpload *upload = GST_VAAPIUPLOAD (gst_pad_get_parent_element (pad));
801 GST_DEBUG ("sharing display %p", upload->display);
803 if (gst_vaapi_reply_to_query (query, upload->display))
806 res = gst_pad_query_default (pad, query);
808 g_object_unref (upload);