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.
31 #include "gst/vaapi/sysdeps.h"
33 #include <gst/video/video.h>
34 #include <gst/video/videocontext.h>
35 #include <gst/vaapi/gstvaapivideobuffer.h>
37 #include "gstvaapiupload.h"
38 #include "gstvaapipluginutil.h"
39 #include "gstvaapipluginbuffer.h"
41 #define GST_PLUGIN_NAME "vaapiupload"
42 #define GST_PLUGIN_DESC "A video to VA flow filter"
44 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapiupload);
45 #define GST_CAT_DEFAULT gst_debug_vaapiupload
47 /* ElementFactory information */
48 static const GstElementDetails gst_vaapiupload_details =
50 "VA-API colorspace converter",
51 "Filter/Converter/Video",
53 "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
55 /* Default templates */
56 static const char gst_vaapiupload_yuv_caps_str[] =
58 "width = (int) [ 1, MAX ], "
59 "height = (int) [ 1, MAX ]; ";
61 static const char gst_vaapiupload_vaapi_caps_str[] =
62 GST_VAAPI_SURFACE_CAPS;
64 static GstStaticPadTemplate gst_vaapiupload_sink_factory =
65 GST_STATIC_PAD_TEMPLATE(
69 GST_STATIC_CAPS(gst_vaapiupload_yuv_caps_str));
71 static GstStaticPadTemplate gst_vaapiupload_src_factory =
72 GST_STATIC_PAD_TEMPLATE(
76 GST_STATIC_CAPS(gst_vaapiupload_vaapi_caps_str));
79 gst_vaapiupload_implements_iface_init(GstImplementsInterfaceClass *iface);
82 gst_video_context_interface_init(GstVideoContextInterface *iface);
84 #define GstVideoContextClass GstVideoContextInterface
85 G_DEFINE_TYPE_WITH_CODE(
88 GST_TYPE_BASE_TRANSFORM,
89 G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
90 gst_vaapiupload_implements_iface_init);
91 G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
92 gst_video_context_interface_init))
95 * Direct rendering levels (direct-rendering)
96 * 0: upstream allocated YUV pixels
97 * 1: vaapiupload allocated YUV pixels (mapped from VA image)
98 * 2: vaapiupload allocated YUV pixels (mapped from VA surface)
100 #define DIRECT_RENDERING_DEFAULT 2
105 PROP_DIRECT_RENDERING,
109 gst_vaapiupload_start(GstBaseTransform *trans);
112 gst_vaapiupload_stop(GstBaseTransform *trans);
115 gst_vaapiupload_transform(
116 GstBaseTransform *trans,
122 gst_vaapiupload_transform_caps(
123 GstBaseTransform *trans,
124 GstPadDirection direction,
129 gst_vaapiupload_set_caps(
130 GstBaseTransform *trans,
136 gst_vaapiupload_get_unit_size(
137 GstBaseTransform *trans,
143 gst_vaapiupload_sinkpad_buffer_alloc(
152 gst_vaapiupload_prepare_output_buffer(
153 GstBaseTransform *trans,
161 gst_vaapiupload_query(
166 /* GstImplementsInterface interface */
169 gst_vaapiupload_implements_interface_supported(
170 GstImplementsInterface *iface,
174 return (type == GST_TYPE_VIDEO_CONTEXT);
178 gst_vaapiupload_implements_iface_init(GstImplementsInterfaceClass *iface)
180 iface->supported = gst_vaapiupload_implements_interface_supported;
183 /* GstVideoContext interface */
186 gst_vaapiupload_set_video_context(GstVideoContext *context, const gchar *type,
189 GstVaapiUpload *upload = GST_VAAPIUPLOAD (context);
190 gst_vaapi_set_display (type, value, &upload->display);
194 gst_video_context_interface_init(GstVideoContextInterface *iface)
196 iface->set_context = gst_vaapiupload_set_video_context;
200 gst_vaapiupload_destroy(GstVaapiUpload *upload)
202 g_clear_object(&upload->images);
203 g_clear_object(&upload->surfaces);
204 g_clear_object(&upload->display);
208 gst_vaapiupload_finalize(GObject *object)
210 gst_vaapiupload_destroy(GST_VAAPIUPLOAD(object));
212 G_OBJECT_CLASS(gst_vaapiupload_parent_class)->finalize(object);
217 gst_vaapiupload_set_property(
224 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(object);
227 case PROP_DIRECT_RENDERING:
228 GST_OBJECT_LOCK(upload);
229 upload->direct_rendering = g_value_get_uint(value);
230 GST_OBJECT_UNLOCK(upload);
233 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
239 gst_vaapiupload_get_property(
246 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(object);
249 case PROP_DIRECT_RENDERING:
250 g_value_set_uint(value, upload->direct_rendering);
253 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
259 gst_vaapiupload_class_init(GstVaapiUploadClass *klass)
261 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
262 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
263 GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
264 GstPadTemplate *pad_template;
266 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapiupload,
267 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
269 object_class->finalize = gst_vaapiupload_finalize;
270 object_class->set_property = gst_vaapiupload_set_property;
271 object_class->get_property = gst_vaapiupload_get_property;
273 trans_class->start = gst_vaapiupload_start;
274 trans_class->stop = gst_vaapiupload_stop;
275 trans_class->transform = gst_vaapiupload_transform;
276 trans_class->transform_caps = gst_vaapiupload_transform_caps;
277 trans_class->set_caps = gst_vaapiupload_set_caps;
278 trans_class->get_unit_size = gst_vaapiupload_get_unit_size;
279 trans_class->prepare_output_buffer = gst_vaapiupload_prepare_output_buffer;
281 gst_element_class_set_details_simple(
283 gst_vaapiupload_details.longname,
284 gst_vaapiupload_details.klass,
285 gst_vaapiupload_details.description,
286 gst_vaapiupload_details.author
290 pad_template = gst_static_pad_template_get(&gst_vaapiupload_sink_factory);
291 gst_element_class_add_pad_template(element_class, pad_template);
292 gst_object_unref(pad_template);
295 pad_template = gst_static_pad_template_get(&gst_vaapiupload_src_factory);
296 gst_element_class_add_pad_template(element_class, pad_template);
297 gst_object_unref(pad_template);
300 * GstVaapiUpload:direct-rendering:
302 * Selects the direct rendering level.
304 * <listitem override="0">
305 * Disables direct rendering.
308 * Enables direct rendering to the output buffer. i.e. this
309 * tries to use a single buffer for both sink and src pads.
312 * Enables direct rendering to the underlying surface. i.e. with
313 * drivers supporting vaDeriveImage(), the output surface pixels
314 * will be modified directly.
318 g_object_class_install_property
320 PROP_DIRECT_RENDERING,
321 g_param_spec_uint("direct-rendering",
323 "Direct rendering level",
325 DIRECT_RENDERING_DEFAULT,
330 gst_vaapiupload_init(GstVaapiUpload *upload)
332 GstPad *sinkpad, *srcpad;
334 upload->display = NULL;
335 upload->images = NULL;
336 upload->images_reset = FALSE;
337 upload->image_width = 0;
338 upload->image_height = 0;
339 upload->surfaces = NULL;
340 upload->surfaces_reset = FALSE;
341 upload->surface_width = 0;
342 upload->surface_height = 0;
343 upload->direct_rendering_caps = 0;
344 upload->direct_rendering = G_MAXUINT32;
346 /* Override buffer allocator on sink pad */
347 sinkpad = gst_element_get_static_pad(GST_ELEMENT(upload), "sink");
348 gst_pad_set_bufferalloc_function(
350 gst_vaapiupload_sinkpad_buffer_alloc
352 gst_pad_set_query_function(sinkpad, gst_vaapiupload_query);
353 g_object_unref(sinkpad);
355 /* Override query on src pad */
356 srcpad = gst_element_get_static_pad(GST_ELEMENT(upload), "src");
357 gst_pad_set_query_function(srcpad, gst_vaapiupload_query);
358 g_object_unref(srcpad);
361 static inline gboolean
362 gst_vaapiupload_ensure_display(GstVaapiUpload *upload)
364 return gst_vaapi_ensure_display(upload, GST_VAAPI_DISPLAY_TYPE_ANY,
369 gst_vaapiupload_start(GstBaseTransform *trans)
371 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
373 if (!gst_vaapiupload_ensure_display(upload))
379 gst_vaapiupload_stop(GstBaseTransform *trans)
381 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
383 g_clear_object(&upload->display);
389 gst_vaapiupload_transform(
390 GstBaseTransform *trans,
395 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
396 GstVaapiVideoBuffer *vbuffer;
397 GstVaapiSurface *surface;
398 GstVaapiImage *image;
399 GstCaps *buffer_caps;
401 GstVaapiImageFormat buffer_format;
402 gboolean format_changed;
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 if (convert->direct_rendering)
626 gst_vaapiupload_ensure_direct_rendering_caps(upload, incaps);
627 dr = MIN(upload->direct_rendering, upload->direct_rendering_caps);
628 if (upload->direct_rendering != dr) {
629 upload->direct_rendering = dr;
634 gst_vaapiupload_set_caps(
635 GstBaseTransform *trans,
640 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
642 if (!gst_vaapiupload_negotiate_buffers(upload, incaps, outcaps))
649 gst_vaapiupload_get_unit_size(
650 GstBaseTransform *trans,
655 GstStructure * const structure = gst_caps_get_structure(caps, 0);
656 GstVideoFormat format;
659 if (gst_structure_has_name(structure, GST_VAAPI_SURFACE_CAPS_NAME))
662 if (!gst_video_format_parse_caps(caps, &format, &width, &height))
664 *size = gst_video_format_get_size(format, width, height);
670 gst_vaapiupload_buffer_alloc(
671 GstBaseTransform *trans,
677 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
678 GstBuffer *buffer = NULL;
679 GstVaapiImage *image = NULL;
680 GstVaapiSurface *surface = NULL;
681 GstVaapiVideoBuffer *vbuffer;
683 /* Check if we can use direct-rendering */
684 if (!gst_vaapiupload_negotiate_buffers(upload, caps, caps))
686 if (!upload->direct_rendering)
689 switch (upload->direct_rendering) {
691 buffer = gst_vaapi_video_buffer_new_from_pool(upload->surfaces);
694 vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
696 surface = gst_vaapi_video_buffer_get_surface(vbuffer);
697 image = gst_vaapi_surface_derive_image(surface);
698 if (image && gst_vaapi_image_get_data_size(image) == size) {
699 gst_vaapi_video_buffer_set_image(vbuffer, image);
700 g_object_unref(image); /* video buffer owns an extra reference */
704 /* We can't use the derive-image optimization. Disable it. */
705 upload->direct_rendering = 1;
706 gst_buffer_unref(buffer);
710 buffer = gst_vaapi_video_buffer_new_from_pool(upload->images);
713 vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
715 image = gst_vaapi_video_buffer_get_image(vbuffer);
720 if (!gst_vaapi_image_map(image))
723 GST_BUFFER_DATA(buffer) = gst_vaapi_image_get_plane(image, 0);
724 GST_BUFFER_SIZE(buffer) = gst_vaapi_image_get_data_size(image);
726 gst_buffer_set_caps(buffer, caps);
731 /* We can't use the inout-buffers optimization. Disable it. */
732 GST_DEBUG("disable in/out buffer optimization");
734 gst_buffer_unref(buffer);
735 upload->direct_rendering = 0;
740 gst_vaapiupload_sinkpad_buffer_alloc(
748 GstBaseTransform *trans;
751 trans = GST_BASE_TRANSFORM(gst_pad_get_parent_element(pad));
753 return GST_FLOW_UNEXPECTED;
755 ret = gst_vaapiupload_buffer_alloc(trans, size, caps, pbuf);
756 g_object_unref(trans);
761 gst_vaapiupload_prepare_output_buffer(
762 GstBaseTransform *trans,
769 GstVaapiUpload * const upload = GST_VAAPIUPLOAD(trans);
770 GstBuffer *buffer = NULL;
772 if (upload->direct_rendering == 2) {
773 if (GST_VAAPI_IS_VIDEO_BUFFER(inbuf)) {
774 buffer = gst_vaapi_video_buffer_new_from_buffer(inbuf);
775 GST_BUFFER_SIZE(buffer) = size;
778 GST_DEBUG("upstream element destroyed our in/out buffer");
779 upload->direct_rendering = 1;
784 buffer = gst_vaapi_video_buffer_new_from_pool(upload->surfaces);
786 return GST_FLOW_UNEXPECTED;
787 gst_buffer_set_caps(buffer, caps);
795 gst_vaapiupload_query(GstPad *pad, GstQuery *query)
797 GstVaapiUpload *upload = GST_VAAPIUPLOAD (gst_pad_get_parent_element (pad));
800 GST_DEBUG ("sharing display %p", upload->display);
802 if (gst_vaapi_reply_to_query (query, upload->display))
805 res = gst_pad_query_default (pad, query);
807 g_object_unref (upload);