/* * gstvaapipluginbase.c - Base GStreamer VA-API Plugin element * * Copyright (C) 2010-2011 Splitted-Desktop Systems * Author: Gwenole Beauchesne * Copyright (C) 2011-2014 Intel Corporation * Author: Gwenole Beauchesne * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "gstcompat.h" #include #include #include "gstvaapipluginbase.h" #include "gstvaapipluginutil.h" #include "gstvaapivideocontext.h" #include "gstvaapivideometa.h" #include "gstvaapivideobufferpool.h" #include /* Default debug category is from the subclass */ #define GST_CAT_DEFAULT (plugin->debug_category) static gpointer plugin_parent_class = NULL; /* GstVideoContext interface */ static void plugin_set_display (GstVaapiPluginBase * plugin, GstVaapiDisplay * display) { const gchar *const display_name = gst_vaapi_display_get_display_name (display); if (plugin->display_name && g_strcmp0 (plugin->display_name, display_name)) { GST_DEBUG_OBJECT (plugin, "incompatible display name '%s', requested '%s'", display_name, plugin->display_name); gst_vaapi_display_replace (&plugin->display, NULL); } else { GST_INFO_OBJECT (plugin, "set display %p", display); gst_vaapi_display_replace (&plugin->display, display); plugin->display_type = gst_vaapi_display_get_display_type (display); gst_vaapi_plugin_base_set_display_name (plugin, display_name); } gst_vaapi_display_unref (display); } static void plugin_set_context (GstElement * element, GstContext * context) { GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element); GstElementClass *element_class = GST_ELEMENT_CLASS (plugin_parent_class); GstVaapiDisplay *display = NULL; if (gst_vaapi_video_context_get_display (context, &display)) plugin_set_display (plugin, display); if (element_class->set_context) element_class->set_context (element, context); } void gst_vaapi_plugin_base_init_interfaces (GType g_define_type_id) { } static gboolean default_has_interface (GstVaapiPluginBase * plugin, GType type) { return FALSE; } static void default_display_changed (GstVaapiPluginBase * plugin) { } static gboolean plugin_update_sinkpad_info_from_buffer (GstVaapiPluginBase * plugin, GstBuffer * buf) { GstVideoInfo *const vip = &plugin->sinkpad_info; GstVideoMeta *vmeta; guint i; vmeta = gst_buffer_get_video_meta (buf); if (!vmeta) return TRUE; if (GST_VIDEO_INFO_FORMAT (vip) != vmeta->format || GST_VIDEO_INFO_WIDTH (vip) != vmeta->width || GST_VIDEO_INFO_HEIGHT (vip) != vmeta->height || GST_VIDEO_INFO_N_PLANES (vip) != vmeta->n_planes) return FALSE; for (i = 0; i < GST_VIDEO_INFO_N_PLANES (vip); ++i) { GST_VIDEO_INFO_PLANE_OFFSET (vip, i) = vmeta->offset[i]; GST_VIDEO_INFO_PLANE_STRIDE (vip, i) = vmeta->stride[i]; } GST_VIDEO_INFO_SIZE (vip) = gst_buffer_get_size (buf); return TRUE; } static gboolean is_dma_buffer (GstBuffer * buf) { GstMemory *mem; if (gst_buffer_n_memory (buf) < 1) return FALSE; mem = gst_buffer_peek_memory (buf, 0); if (!mem || !gst_is_dmabuf_memory (mem)) return FALSE; return TRUE; } static gboolean plugin_bind_dma_to_vaapi_buffer (GstVaapiPluginBase * plugin, GstBuffer * inbuf, GstBuffer * outbuf) { GstVideoInfo *const vip = &plugin->sinkpad_info; GstVaapiVideoMeta *meta; GstVaapiSurface *surface; GstVaapiSurfaceProxy *proxy; gint fd; fd = gst_dmabuf_memory_get_fd (gst_buffer_peek_memory (inbuf, 0)); if (fd < 0) return FALSE; if (!plugin_update_sinkpad_info_from_buffer (plugin, inbuf)) goto error_update_sinkpad_info; meta = gst_buffer_get_vaapi_video_meta (outbuf); g_return_val_if_fail (meta != NULL, FALSE); surface = gst_vaapi_surface_new_with_dma_buf_handle (plugin->display, fd, GST_VIDEO_INFO_SIZE (vip), GST_VIDEO_INFO_FORMAT (vip), GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip), vip->offset, vip->stride); if (!surface) goto error_create_surface; proxy = gst_vaapi_surface_proxy_new (surface); gst_vaapi_object_unref (surface); if (!proxy) goto error_create_proxy; gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) gst_buffer_unref, (gpointer) gst_buffer_ref (inbuf)); gst_vaapi_video_meta_set_surface_proxy (meta, proxy); gst_vaapi_surface_proxy_unref (proxy); return TRUE; /* ERRORS */ error_update_sinkpad_info: GST_ERROR ("failed to update sink pad video info from video meta"); return FALSE; error_create_surface: GST_ERROR ("failed to create VA surface from dma_buf handle"); return FALSE; error_create_proxy: GST_ERROR ("failed to create VA surface proxy from wrapped VA surface"); return FALSE; } void gst_vaapi_plugin_base_class_init (GstVaapiPluginBaseClass * klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); klass->has_interface = default_has_interface; klass->display_changed = default_display_changed; plugin_parent_class = g_type_class_peek_parent (klass); element_class->set_context = GST_DEBUG_FUNCPTR (plugin_set_context); } void gst_vaapi_plugin_base_init (GstVaapiPluginBase * plugin, GstDebugCategory * debug_category) { plugin->debug_category = debug_category; plugin->display_type = GST_VAAPI_DISPLAY_TYPE_ANY; plugin->display_type_req = GST_VAAPI_DISPLAY_TYPE_ANY; /* sink pad */ plugin->sinkpad = gst_element_get_static_pad (GST_ELEMENT (plugin), "sink"); gst_video_info_init (&plugin->sinkpad_info); /* src pad */ if (!(GST_OBJECT_FLAGS (plugin) & GST_ELEMENT_FLAG_SINK)) plugin->srcpad = gst_element_get_static_pad (GST_ELEMENT (plugin), "src"); gst_video_info_init (&plugin->srcpad_info); } void gst_vaapi_plugin_base_finalize (GstVaapiPluginBase * plugin) { gst_vaapi_plugin_base_close (plugin); g_free (plugin->display_name); if (plugin->sinkpad) gst_object_unref (plugin->sinkpad); if (plugin->srcpad) gst_object_unref (plugin->srcpad); } /** * gst_vaapi_plugin_base_open: * @plugin: a #GstVaapiPluginBase * * Allocates any internal resources needed for correct operation from * the subclass. * * Returns: %TRUE if successful, %FALSE otherwise. */ gboolean gst_vaapi_plugin_base_open (GstVaapiPluginBase * plugin) { return TRUE; } /** * gst_vaapi_plugin_base_close: * @plugin: a #GstVaapiPluginBase * * Deallocates all internal resources that were allocated so * far. i.e. put the base plugin object into a clean state. */ void gst_vaapi_plugin_base_close (GstVaapiPluginBase * plugin) { gst_vaapi_display_replace (&plugin->display, NULL); gst_object_replace (&plugin->gl_context, NULL); gst_caps_replace (&plugin->sinkpad_caps, NULL); plugin->sinkpad_caps_changed = FALSE; gst_video_info_init (&plugin->sinkpad_info); if (plugin->sinkpad_buffer_pool) { gst_object_unref (plugin->sinkpad_buffer_pool); plugin->sinkpad_buffer_pool = NULL; } g_clear_object (&plugin->srcpad_buffer_pool); gst_caps_replace (&plugin->srcpad_caps, NULL); plugin->srcpad_caps_changed = FALSE; gst_video_info_init (&plugin->srcpad_info); gst_caps_replace (&plugin->allowed_raw_caps, NULL); } /** * gst_vaapi_plugin_base_has_display_type: * @plugin: a #GstVaapiPluginBase * @display_type_req: the desired #GstVaapiDisplayType * * Checks whether the @plugin elements already has a #GstVaapiDisplay * instance compatible with type @display_type_req. * * Return value: %TRUE if @plugin has a compatible display, %FALSE otherwise */ gboolean gst_vaapi_plugin_base_has_display_type (GstVaapiPluginBase * plugin, GstVaapiDisplayType display_type_req) { GstVaapiDisplayType display_type; if (!plugin->display) return FALSE; display_type = plugin->display_type; if (gst_vaapi_display_type_is_compatible (display_type, display_type_req)) return TRUE; display_type = gst_vaapi_display_get_class_type (plugin->display); if (gst_vaapi_display_type_is_compatible (display_type, display_type_req)) return TRUE; return FALSE; } /** * gst_vaapi_plugin_base_set_display_type: * @plugin: a #GstVaapiPluginBase * @display_type: the new request #GstVaapiDisplayType * * Requests a new display type. The change is effective at the next * call to gst_vaapi_plugin_base_ensure_display(). */ void gst_vaapi_plugin_base_set_display_type (GstVaapiPluginBase * plugin, GstVaapiDisplayType display_type) { plugin->display_type_req = display_type; } /** * gst_vaapi_plugin_base_set_display_name: * @plugin: a #GstVaapiPluginBase * @display_name: the new display name to match * * Sets the name of the display to look for. The change is effective * at the next call to gst_vaapi_plugin_base_ensure_display(). */ void gst_vaapi_plugin_base_set_display_name (GstVaapiPluginBase * plugin, const gchar * display_name) { g_free (plugin->display_name); plugin->display_name = g_strdup (display_name); } /** * gst_vaapi_plugin_base_ensure_display: * @plugin: a #GstVaapiPluginBase * * Ensures the display stored in @plugin complies with the requested * display type constraints. * * Returns: %TRUE if the display was created to match the requested * type, %FALSE otherwise. */ gboolean gst_vaapi_plugin_base_ensure_display (GstVaapiPluginBase * plugin) { if (gst_vaapi_plugin_base_has_display_type (plugin, plugin->display_type_req)) return TRUE; gst_vaapi_display_replace (&plugin->display, NULL); if (!gst_vaapi_ensure_display (GST_ELEMENT (plugin), plugin->display_type_req)) return FALSE; plugin->display_type = gst_vaapi_display_get_display_type (plugin->display); GST_VAAPI_PLUGIN_BASE_GET_CLASS (plugin)->display_changed (plugin); return TRUE; } /* Checks whether the supplied pad peer element supports DMABUF sharing */ /* XXX: this is a workaround to the absence of any proposer way to specify DMABUF memory capsfeatures or bufferpool option to downstream */ static gboolean has_dmabuf_capable_peer (GstVaapiPluginBase * plugin, GstPad * pad) { GstPad *other_pad = NULL; GstElement *element = NULL; gchar *element_name = NULL; gboolean is_dmabuf_capable = FALSE; gint v; gst_object_ref (pad); for (;;) { other_pad = gst_pad_get_peer (pad); gst_object_unref (pad); if (!other_pad) break; element = gst_pad_get_parent_element (other_pad); gst_object_unref (other_pad); if (!element) break; if (GST_IS_PUSH_SRC (element)) { element_name = gst_element_get_name (element); if (!element_name || sscanf (element_name, "v4l2src%d", &v) != 1) break; v = 0; g_object_get (element, "io-mode", &v, NULL); is_dmabuf_capable = v == 5; /* "dmabuf-import" enum value */ break; } else if (GST_IS_BASE_TRANSFORM (element)) { element_name = gst_element_get_name (element); if (!element_name || sscanf (element_name, "capsfilter%d", &v) != 1) break; pad = gst_element_get_static_pad (element, "sink"); if (!pad) break; } else break; g_free (element_name); element_name = NULL; g_clear_object (&element); } g_free (element_name); g_clear_object (&element); return is_dmabuf_capable; } /** * ensure_sinkpad_buffer_pool: * @plugin: a #GstVaapiPluginBase * @caps: the initial #GstCaps for the resulting buffer pool * * Makes sure the sink pad video buffer pool is created with the * appropriate @caps. * * Returns: %TRUE if successful, %FALSE otherwise. */ static gboolean ensure_sinkpad_buffer_pool (GstVaapiPluginBase * plugin, GstCaps * caps) { GstBufferPool *pool; GstCaps *pool_caps; GstStructure *config; GstVideoInfo vi; gboolean need_pool; if (!gst_vaapi_plugin_base_ensure_display (plugin)) return FALSE; if (plugin->sinkpad_buffer_pool) { config = gst_buffer_pool_get_config (plugin->sinkpad_buffer_pool); gst_buffer_pool_config_get_params (config, &pool_caps, NULL, NULL, NULL); need_pool = !gst_caps_is_equal (caps, pool_caps); gst_structure_free (config); if (!need_pool) return TRUE; g_clear_object (&plugin->sinkpad_buffer_pool); plugin->sinkpad_buffer_size = 0; } pool = gst_vaapi_video_buffer_pool_new (plugin->display); if (!pool) goto error_create_pool; gst_video_info_init (&vi); gst_video_info_from_caps (&vi, caps); if (GST_VIDEO_INFO_FORMAT (&vi) == GST_VIDEO_FORMAT_ENCODED) { GST_DEBUG ("assume video buffer pool format is NV12"); gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_NV12, GST_VIDEO_INFO_WIDTH (&vi), GST_VIDEO_INFO_HEIGHT (&vi)); } plugin->sinkpad_buffer_size = vi.size; config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, caps, plugin->sinkpad_buffer_size, 0, 0); gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META); gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); if (!gst_buffer_pool_set_config (pool, config)) goto error_pool_config; plugin->sinkpad_buffer_pool = pool; return TRUE; /* ERRORS */ error_create_pool: { GST_ERROR ("failed to create buffer pool"); return FALSE; } error_pool_config: { GST_ERROR ("failed to reset buffer pool config"); gst_object_unref (pool); return FALSE; } } /** * gst_vaapi_plugin_base_set_caps: * @plugin: a #GstVaapiPluginBase * @incaps: the sink pad (input) caps * @outcaps: the src pad (output) caps * * Notifies the base plugin object of the new input and output caps, * obtained from the subclass. * * Returns: %TRUE if the update of caps was successful, %FALSE otherwise. */ gboolean gst_vaapi_plugin_base_set_caps (GstVaapiPluginBase * plugin, GstCaps * incaps, GstCaps * outcaps) { if (incaps && incaps != plugin->sinkpad_caps) { gst_caps_replace (&plugin->sinkpad_caps, incaps); if (!gst_video_info_from_caps (&plugin->sinkpad_info, incaps)) return FALSE; plugin->sinkpad_caps_changed = TRUE; plugin->sinkpad_caps_is_raw = !gst_caps_has_vaapi_surface (incaps); } if (outcaps && outcaps != plugin->srcpad_caps) { gst_caps_replace (&plugin->srcpad_caps, outcaps); if (!gst_video_info_from_caps (&plugin->srcpad_info, outcaps)) return FALSE; plugin->srcpad_caps_changed = TRUE; } if (!ensure_sinkpad_buffer_pool (plugin, plugin->sinkpad_caps)) return FALSE; return TRUE; } /** * gst_vaapi_plugin_base_propose_allocation: * @plugin: a #GstVaapiPluginBase * @query: the allocation query to configure * * Proposes allocation parameters to the upstream elements. * * Returns: %TRUE if successful, %FALSE otherwise. */ gboolean gst_vaapi_plugin_base_propose_allocation (GstVaapiPluginBase * plugin, GstQuery * query) { GstCaps *caps = NULL; gboolean need_pool; gst_query_parse_allocation (query, &caps, &need_pool); if (need_pool) { if (!caps) goto error_no_caps; if (!ensure_sinkpad_buffer_pool (plugin, caps)) return FALSE; gst_query_add_allocation_pool (query, plugin->sinkpad_buffer_pool, plugin->sinkpad_buffer_size, 0, 0); if (has_dmabuf_capable_peer (plugin, plugin->sinkpad)) { GstStructure *const config = gst_buffer_pool_get_config (plugin->sinkpad_buffer_pool); gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_DMABUF_MEMORY); if (!gst_buffer_pool_set_config (plugin->sinkpad_buffer_pool, config)) goto error_pool_config; } } gst_query_add_allocation_meta (query, GST_VAAPI_VIDEO_META_API_TYPE, NULL); gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); return TRUE; /* ERRORS */ error_no_caps: { GST_INFO_OBJECT (plugin, "no caps specified"); return FALSE; } error_pool_config: { GST_ERROR_OBJECT (plugin, "failed to reset buffer pool config"); return FALSE; } } /* XXXX: GStreamer 1.2 doesn't check, in gst_buffer_pool_set_config() if the config option is already set */ static inline gboolean gst_vaapi_plugin_base_set_pool_config (GstBufferPool * pool, const gchar * option) { GstStructure *config; config = gst_buffer_pool_get_config (pool); if (!gst_buffer_pool_config_has_option (config, option)) { gst_buffer_pool_config_add_option (config, option); return gst_buffer_pool_set_config (pool, config); } return TRUE; } /** * gst_vaapi_plugin_base_decide_allocation: * @plugin: a #GstVaapiPluginBase * @query: the allocation query to parse * @feature: the desired #GstVaapiCapsFeature, or zero to find the * preferred one * * Decides allocation parameters for the downstream elements. * * Returns: %TRUE if successful, %FALSE otherwise. */ gboolean gst_vaapi_plugin_base_decide_allocation (GstVaapiPluginBase * plugin, GstQuery * query, guint feature) { GstCaps *caps = NULL; GstBufferPool *pool; GstStructure *config; GstVideoInfo vi; guint size, min, max; gboolean update_pool = FALSE; gboolean has_video_meta = FALSE; gboolean has_video_alignment = FALSE; #if (USE_GLX || USE_EGL) gboolean has_texture_upload_meta = FALSE; guint idx; #endif g_return_val_if_fail (plugin->display != NULL, FALSE); gst_query_parse_allocation (query, &caps, NULL); /* We don't need any GL context beyond this point if not requested so explicitly through GstVideoGLTextureUploadMeta */ gst_object_replace (&plugin->gl_context, NULL); if (!caps) goto error_no_caps; if (!feature) feature = gst_vaapi_find_preferred_caps_feature (plugin->srcpad, GST_VIDEO_FORMAT_ENCODED, NULL); has_video_meta = gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); #if (USE_GLX || USE_EGL) has_texture_upload_meta = gst_query_find_allocation_meta (query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx) && (feature == GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META); #if USE_GST_GL_HELPERS if (has_texture_upload_meta) { const GstStructure *params; GstObject *gl_context; gst_query_parse_nth_allocation_meta (query, idx, ¶ms); if (params) { if (gst_structure_get (params, "gst.gl.GstGLContext", GST_GL_TYPE_CONTEXT, &gl_context, NULL) && gl_context) { gst_vaapi_plugin_base_set_gl_context (plugin, gl_context); gst_object_unref (gl_context); } } } #endif #endif /* Make sure the display we pass down to the buffer pool is actually the expected one, especially when the downstream element requires a GLX or EGL display */ if (!gst_vaapi_plugin_base_ensure_display (plugin)) goto error_ensure_display; gst_video_info_init (&vi); gst_video_info_from_caps (&vi, caps); if (GST_VIDEO_INFO_FORMAT (&vi) == GST_VIDEO_FORMAT_ENCODED) gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_I420, GST_VIDEO_INFO_WIDTH (&vi), GST_VIDEO_INFO_HEIGHT (&vi)); if (gst_query_get_n_allocation_pools (query) > 0) { gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); update_pool = TRUE; size = MAX (size, vi.size); if (pool) { /* Check whether downstream element proposed a bufferpool but did not provide a correct propose_allocation() implementation */ has_video_alignment = gst_buffer_pool_has_option (pool, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); } } else { pool = NULL; size = vi.size; min = max = 0; } /* GstVaapiVideoMeta is mandatory, and this implies VA surface memory */ if (!pool || !gst_buffer_pool_has_option (pool, GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META)) { GST_INFO_OBJECT (plugin, "%s. Making a new pool", pool == NULL ? "No pool" : "Pool hasn't GstVaapiVideoMeta"); if (pool) gst_object_unref (pool); pool = gst_vaapi_video_buffer_pool_new (plugin->display); if (!pool) goto error_create_pool; config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, caps, size, min, max); gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META); if (!gst_buffer_pool_set_config (pool, config)) goto config_failed; } /* Check whether GstVideoMeta, or GstVideoAlignment, is needed (raw video) */ if (has_video_meta) { if (!gst_vaapi_plugin_base_set_pool_config (pool, GST_BUFFER_POOL_OPTION_VIDEO_META)) goto config_failed; } else if (has_video_alignment) { if (!gst_vaapi_plugin_base_set_pool_config (pool, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) goto config_failed; } /* GstVideoGLTextureUploadMeta (OpenGL) */ #if (USE_GLX || USE_EGL) if (has_texture_upload_meta) { if (!gst_vaapi_plugin_base_set_pool_config (pool, GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META)) goto config_failed; } #endif if (update_pool) gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); else gst_query_add_allocation_pool (query, pool, size, min, max); g_clear_object (&plugin->srcpad_buffer_pool); plugin->srcpad_buffer_pool = pool; return TRUE; /* ERRORS */ error_no_caps: { GST_ERROR_OBJECT (plugin, "no caps specified"); return FALSE; } error_ensure_display: { GST_ERROR_OBJECT (plugin, "failed to ensure display of type %d", plugin->display_type_req); return FALSE; } error_create_pool: { GST_ERROR_OBJECT (plugin, "failed to create buffer pool"); return FALSE; } config_failed: { if (pool) gst_object_unref (pool); GST_ELEMENT_ERROR (plugin, RESOURCE, SETTINGS, ("Failed to configure the buffer pool"), ("Configuration is most likely invalid, please report this issue.")); return FALSE; } } /** * gst_vaapi_plugin_base_get_input_buffer: * @plugin: a #GstVaapiPluginBase * @incaps: the sink pad (input) buffer * @outbuf_ptr: the pointer to location to the VA surface backed buffer * * Acquires the sink pad (input) buffer as a VA surface backed * buffer. This is mostly useful for raw YUV buffers, as source * buffers that are already backed as a VA surface are passed * verbatim. * * Returns: #GST_FLOW_OK if the buffer could be acquired */ GstFlowReturn gst_vaapi_plugin_base_get_input_buffer (GstVaapiPluginBase * plugin, GstBuffer * inbuf, GstBuffer ** outbuf_ptr) { GstVaapiVideoMeta *meta; GstBuffer *outbuf; GstVideoFrame src_frame, out_frame; gboolean success; g_return_val_if_fail (inbuf != NULL, GST_FLOW_ERROR); g_return_val_if_fail (outbuf_ptr != NULL, GST_FLOW_ERROR); meta = gst_buffer_get_vaapi_video_meta (inbuf); if (meta) { *outbuf_ptr = gst_buffer_ref (inbuf); return GST_FLOW_OK; } if (!plugin->sinkpad_caps_is_raw) goto error_invalid_buffer; if (!plugin->sinkpad_buffer_pool) goto error_no_pool; if (!gst_buffer_pool_set_active (plugin->sinkpad_buffer_pool, TRUE)) goto error_active_pool; outbuf = NULL; if (gst_buffer_pool_acquire_buffer (plugin->sinkpad_buffer_pool, &outbuf, NULL) != GST_FLOW_OK) goto error_create_buffer; if (is_dma_buffer (inbuf)) { if (!plugin_bind_dma_to_vaapi_buffer (plugin, inbuf, outbuf)) goto error_bind_dma_buffer; goto done; } if (!gst_video_frame_map (&src_frame, &plugin->sinkpad_info, inbuf, GST_MAP_READ)) goto error_map_src_buffer; if (!gst_video_frame_map (&out_frame, &plugin->sinkpad_info, outbuf, GST_MAP_WRITE)) goto error_map_dst_buffer; success = gst_video_frame_copy (&out_frame, &src_frame); gst_video_frame_unmap (&out_frame); gst_video_frame_unmap (&src_frame); if (!success) goto error_copy_buffer; done: gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1); *outbuf_ptr = outbuf; return GST_FLOW_OK; /* ERRORS */ error_no_pool: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("no buffer pool was negotiated"), ("no buffer pool was negotiated")); return GST_FLOW_ERROR; } error_active_pool: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("failed to activate buffer pool"), ("failed to activate buffer pool")); return GST_FLOW_ERROR; } error_map_dst_buffer: { gst_video_frame_unmap (&src_frame); // fall-through } error_map_src_buffer: { GST_WARNING ("failed to map buffer"); gst_buffer_unref (outbuf); return GST_FLOW_NOT_SUPPORTED; } /* ERRORS */ error_invalid_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("failed to validate source buffer"), ("failed to validate source buffer")); return GST_FLOW_ERROR; } error_create_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"), ("failed to create buffer")); return GST_FLOW_ERROR; } error_bind_dma_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"), ("failed to bind dma_buf to VA surface buffer")); gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } error_copy_buffer: { GST_WARNING ("failed to upload buffer to VA surface"); gst_buffer_unref (outbuf); return GST_FLOW_NOT_SUPPORTED; } } /** * gst_vaapi_plugin_base_set_gl_context: * @plugin: a #GstVaapiPluginBase * @object: the new GL context from downstream * * Registers the new GL context. The change is effective at the next * call to gst_vaapi_plugin_base_ensure_display(), where the * underlying display object could be re-allocated to fit the GL * context needs */ void gst_vaapi_plugin_base_set_gl_context (GstVaapiPluginBase * plugin, GstObject * object) { #if USE_GST_GL_HELPERS GstGLContext *const gl_context = GST_GL_CONTEXT (object); GstVaapiDisplayType display_type; gst_object_replace (&plugin->gl_context, object); switch (gst_gl_context_get_gl_platform (gl_context)) { #if USE_GLX case GST_GL_PLATFORM_GLX: display_type = GST_VAAPI_DISPLAY_TYPE_GLX; break; #endif #if USE_EGL case GST_GL_PLATFORM_EGL: display_type = GST_VAAPI_DISPLAY_TYPE_EGL; break; #endif default: display_type = plugin->display_type; break; } gst_vaapi_plugin_base_set_display_type (plugin, display_type); #endif } static gboolean ensure_allowed_raw_caps (GstVaapiPluginBase * plugin) { GArray *formats, *out_formats; GstVaapiSurface *surface; guint i; GstCaps *out_caps; gboolean ret = FALSE; if (plugin->allowed_raw_caps) return TRUE; out_formats = formats = NULL; surface = NULL; formats = gst_vaapi_display_get_image_formats (plugin->display); if (!formats) goto bail; out_formats = g_array_sized_new (FALSE, FALSE, sizeof (GstVideoFormat), formats->len); if (!out_formats) goto bail; surface = gst_vaapi_surface_new (plugin->display, GST_VAAPI_CHROMA_TYPE_YUV420, 64, 64); if (!surface) goto bail; for (i = 0; i < formats->len; i++) { const GstVideoFormat format = g_array_index (formats, GstVideoFormat, i); GstVaapiImage *image; if (format == GST_VIDEO_FORMAT_UNKNOWN) continue; image = gst_vaapi_image_new (plugin->display, format, 64, 64); if (!image) continue; if (gst_vaapi_surface_put_image (surface, image)) g_array_append_val (out_formats, format); gst_vaapi_object_unref (image); } out_caps = gst_vaapi_video_format_new_template_caps_from_list (out_formats); if (!out_caps) goto bail; gst_caps_replace (&plugin->allowed_raw_caps, out_caps); gst_caps_unref (out_caps); ret = TRUE; bail: if (formats) g_array_unref (formats); if (out_formats) g_array_unref (out_formats); if (surface) gst_vaapi_object_unref (surface); return ret; } /** * gst_vaapi_plugin_base_get_allowed_raw_caps: * @plugin: a #GstVaapiPluginBase * * Returns the raw #GstCaps allowed by the element. * * Returns: the allowed raw #GstCaps or %NULL **/ GstCaps * gst_vaapi_plugin_base_get_allowed_raw_caps (GstVaapiPluginBase * plugin) { if (!ensure_allowed_raw_caps (plugin)) return NULL; return plugin->allowed_raw_caps; }