/* GStreamer * * Copyright (C) 2011 Intel * Copyright (C) 2011 Collabora Ltd. * Author: Nicolas Dufresne * * video-context.c: Video Context interface and helpers * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "videocontext.h" /** * SECTION:gstvideocontext * @short_description: Interface to handle video library context * * The Video Context interface enable sharing video context (such as display * name, X11 Display, VA-API Display, etc) between neighboor elements and the * application. * * The GstVideoContext interface is unstable API and may change in future. * One can define GST_USE_UNSTABLE_API to acknowledge and avoid this warning. * * * * For Element * * This interface shall be implement by group of elements that need to share * a specific video context (like VDPAU, LibVA, OpenGL elements) or by video * sink in order to let the application select appropriate display information * (like the X11 display name) when those sink are auto-plugged. * * * Along with implementing the interface, elements will need to query * neighboor elements or send message to the application when preparing * the context (see gst_video_context_prepare()). They also need to reply * to the neighboors element queries, so the context can be shared without * the application help. * * * Elements that are guarantied to have both upstream and downstream * neighboors element implementing the #GstVideoContext (like the gloverlay * element in gst-plugins-opengl) is not required to also implement the * interface. Relying on neighboors query shall be sufficient (see * gst_video_context_run_query()). * * * The query is an application query with a structure name set to * "prepare-video-context" and an array of supported video context types set * in the field named "types". This query shall be send downstream and * upstream, iterating the pads in order to find neighboors regardless of a * static (sink to src) or a dynamic (src to sink) activation. Element should * used the helpers method gst_video_context_prepare() (or * gst_video_context_run_query() if no GstVideoContext interface) to * correctly execute the query . The result is set using the query helper * functions, the structures fields name being "video-context-type" as * string and "video-context" as a #GValue. * * * If the query is not handled by any neighboor, the element should ask the * application using the "prepare-video-context" message. The application * may then use the interface to set the video context information. If no * context was set, the element shall create one using default configuration. * Elements with multiple src or sink pad shall implement proper locking to * prevent the race of parallel queries being replied. * * * Well known video-context are: "x11-display-name" a string representing the * X11 display to use, "x11-display" the X11 Display structure, "va-display", * the VADisplay structure and more. * * * * * For Application * * In the case there is no neighboor element with video context to share, * the element will first turn toward the application, by sending a * "prepare-video-context" message. This message is sent along with a list * of supported display types. The application can optionally reply to this * message by calling appropriate setter through the #GstVideoContext * interface. If the application supports more then one video context type, * it should choose the first one to occure in the supported list. It's * important to remember that the message is delivered from the streaming * thread, and appropriate locking should be considered. If the application * does not have a video context to share, the element will simply allocate * one base on default settings. Usually, only applications using OpenGL * base sink, or running on special X11 display need to share a video context. * * Applications sharing X11 Display structure should always initialize the * X11 threading support using XInitThreads() as GStreamer will need to * manipulate the display from a separeate threads. * * * * Example using ClutterVideoGstVideoSink * * This example is for user of ClutterGstVideoSink element, the * ClutterGstPlayer object transparently handle this. * * |[ * #if CLUTTER_WINDOWING_X11 * static GstBusSyncReply * on_sync_message (GstBus * bus, GstMessage * message, gpointer user_data) * { * Display *display = user_data; * GstVideoContext *context; * const gchar **types; * * if (gst_video_context_message_parse_prepare (message, &types, &context)) { * gint i; * * for (i = 0; types[i]; i++) { * * if (!strcmp(types[i], "x11-display")) { * gst_video_context_set_context_pointer (context, "x11-display", display); * } * else if (!strcmp(types[i], "x11-display-name")) { * gst_video_context_set_context_string (context, "x11-display-name", * DisplayString (display)); * } else { * continue; * } * * gst_message_unref (message); * return GST_BUS_DROP; * } * } * * return GST_BUS_PASS; * } * #endif * * gint * main (gint argc, gchar **argv) * { * GstBin *pipeline; * GstBus *bus; * * ... * * bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); * * #if CLUTTER_WINDOWING_X11 * gst_bus_set_sync_handler (priv->bus, on_sync_message, * clutter_x11_get_default_display ()); * #endif * * gst_object_unref (GST_OBJECT (priv->bus)); * * ... * } * ]| * * */ G_DEFINE_INTERFACE (GstVideoContext, gst_video_context_iface, G_TYPE_INVALID); static inline GstStructure * gst_video_context_new_structure (const gchar ** types) { return gst_structure_new ("prepare-video-context", "types", G_TYPE_STRV, types, NULL); } static gboolean gst_video_context_pad_query (gpointer item, GValue * value, gpointer user_data) { GstPad *pad = GST_PAD (item); GstQuery *query = user_data; gboolean res; res = gst_pad_peer_query (pad, query); gst_object_unref (pad); if (res) { g_value_set_boolean (value, TRUE); return FALSE; } return TRUE; } static void gst_video_context_iface_default_init (GstVideoContextInterface * iface) { /* default virtual functions */ iface->set_context = NULL; } /** * gst_video_context_set_context: * @context: an element implementing #GstVideoContext * @type: the type of display being set * @value: a #GValue containing the context * * This is a wrapper for the set_context() virtual method. It is suggested to * use one of the helpers to avoid having to manipulate #GValue */ void gst_video_context_set_context (GstVideoContext * context, const gchar * type, const GValue * value) { g_return_if_fail (GST_IS_VIDEO_CONTEXT (context)); g_return_if_fail (GST_VIDEO_CONTEXT_GET_IFACE (context)->set_context); GST_VIDEO_CONTEXT_GET_IFACE (context)->set_context (context, type, value); } /** * gst_video_context_set_context_string: * @context: an element implementing #GstVideoContext * @type: the type of display being set * @string: a string representing the video context * * This helper is commonly used for setting video context represented by a * string like the X11 display name ("x11-display-name")/ */ void gst_video_context_set_context_string (GstVideoContext * context, const gchar * type, const gchar * string) { GValue value = { 0 }; g_value_init (&value, G_TYPE_STRING); g_value_set_string (&value, string); gst_video_context_set_context (context, type, &value); g_value_unset (&value); } /** * gst_video_context_set_context_pointer: * @context: an element implementing #GstVideoContext * @type: the type of display being set * @pointer: a pointer to the video context * * This helper is used for setting video context using a pointer, typically to * a structure like the X11 Display ("x11-display") or the VADisplay * ("vaapi-display"). * * Users of X11 Display should ensure that XInitThreads() was called before * opening the display. * */ void gst_video_context_set_context_pointer (GstVideoContext * context, const gchar * type, gpointer pointer) { GValue value = { 0 }; g_value_init (&value, G_TYPE_POINTER); g_value_set_pointer (&value, pointer); gst_video_context_set_context (context, type, &value); g_value_unset (&value); } /** * gst_video_context_set_context_object: * @context: an element implementing #GstVideoContext * @type: the type of display being set * @object: a #GObject resenting the display * * This is for video context that are #GObject, this helper allow taking * benifit of the #GObject refcounting. It is particularly handy for element * to have refcounting as the order in which element will stop using the * display is not defined. */ void gst_video_context_set_context_object (GstVideoContext * context, const gchar * type, GObject * object) { GValue value = { 0 }; g_return_if_fail (G_IS_OBJECT (object)); g_value_init (&value, G_TYPE_OBJECT); g_value_set_object (&value, object); gst_video_context_set_context (context, type, &value); g_value_unset (&value); } /** * gst_video_context_prepare: * @context: an element implementing #GstVideoContext interface * @types: an array of supported types, prefered first * * This method run "prepare-video-context" custom query dowstream, and * upstream. If * the query has a reply, it sets the context value using * gst_video_context_set_context(). Otherwise, it sends a * "prepare-video-context" message to the application. The element can then * continue video context initialization. */ void gst_video_context_prepare (GstVideoContext * context, const gchar ** types) { GstQuery *query = gst_video_context_query_new (types); /* Check neighborhood, if found call GstVideoContext */ if (gst_video_context_run_query (GST_ELEMENT (context), query)) { const gchar *type = NULL; const GValue *value; gst_video_context_query_parse_value (query, &type, &value); gst_video_context_set_context (context, type, value); } else { /* If no neighbor replyed, query the application */ GstMessage *message; GstStructure *structure; structure = gst_video_context_new_structure (types); message = gst_message_new_element (GST_OBJECT (context), structure); gst_element_post_message (GST_ELEMENT (context), message); } gst_query_unref (query); } /** * gst_video_context_message_parse_prepare: * @message: a #GstMessage * @types: return value for supported types * @context: return value for the element the implements #GstVideoContext * * This helper shall be used by application to simply handling of the * "prepare-video-context" message. * * Rerturns: #FALSE is the message was not valid "prepare-video-context" * element message, otherwise #TRUE with @types and @context set. */ gboolean gst_video_context_message_parse_prepare (GstMessage * message, const gchar *** types, GstVideoContext ** context) { GstObject *src = GST_MESSAGE_SRC (message); const GstStructure *structure; const GValue *value; if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) return FALSE; if (!gst_structure_has_name (message->structure, "prepare-video-context")) return FALSE; if (!GST_IS_VIDEO_CONTEXT (src)) return FALSE; structure = gst_message_get_structure (message); value = gst_structure_get_value (structure, "types"); if (!G_VALUE_HOLDS (value, G_TYPE_STRV)) return FALSE; if (types) *types = g_value_get_boxed (value); if (context) *context = GST_VIDEO_CONTEXT (src); return TRUE; } /** * gst_video_context_query_new: * @types: a string array of video context types * * Create a new custom #GstQuery with structure name "prepare-video-context". */ GstQuery * gst_video_context_query_new (const gchar ** types) { GstStructure *structure = gst_video_context_new_structure (types); return gst_query_new_application (GST_QUERY_CUSTOM, structure); } /** * gst_video_context_run_query: * @element: a #GstElement * @query: a #GstQuery * * This helper runs the query on each downstream, then upstream pads in an * element. This is called by gst_video_context_prepare(). This method is only * used directly within elements that are required to have two neighboors * elements with appropriate video context. This would be the case of * specialized filters that only manipulate non-raw buffers (e.g. * gldeinterlace). Those elements do not have to implement #GstVideoContext * interface. */ gboolean gst_video_context_run_query (GstElement * element, GstQuery * query) { GstIterator *it; GstIteratorFoldFunction func = gst_video_context_pad_query; GValue res = { 0 }; g_value_init (&res, G_TYPE_BOOLEAN); g_value_set_boolean (&res, FALSE); /* Ask downstream neighbor (mainly static pipeline case) */ it = gst_element_iterate_src_pads (element); while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC) gst_iterator_resync (it); gst_iterator_free (it); /* If none, ask upstream neighbor (auto-plugged case) */ if (!g_value_get_boolean (&res)) { it = gst_element_iterate_sink_pads (element); while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC) gst_iterator_resync (it); gst_iterator_free (it); } return g_value_get_boolean (&res); } /** * gst_video_context_query_get_supported_types: * @query: a #GstQuery * * Returns: An array of supported video context types */ const gchar ** gst_video_context_query_get_supported_types (GstQuery * query) { GstStructure *structure = gst_query_get_structure (query); const GValue *value = gst_structure_get_value (structure, "types"); if (G_VALUE_HOLDS (value, G_TYPE_STRV)) return g_value_get_boxed (value); return NULL; } /** * gst_video_context_query_parse_value: * @query: a #GstQuery * @type: return video context type * @value: return video context #GValue * * Helper to extract the video context type and value from a #GstQuery. */ void gst_video_context_query_parse_value (GstQuery * query, const gchar ** type, const GValue ** value) { GstStructure *structure = gst_query_get_structure (query); if (type) *type = gst_structure_get_string (structure, "video-context-type"); if (value) *value = gst_structure_get_value (structure, "video-context"); } /** * gst_video_context_query_set_value: * @query: a #GstQuery * @type: the video context type * @value: a #GValue set with video context * * Helper to set the video context as a #GValue inside the #GstQuery. */ void gst_video_context_query_set_value (GstQuery * query, const gchar * type, GValue * value) { GstStructure *structure = gst_query_get_structure (query); gst_structure_set (structure, "video-context-type", G_TYPE_STRING, type, "video-context", G_TYPE_VALUE, value, NULL); } /** * gst_video_context_query_set_string: * @query: a #GstQuery * @type: the video context type * @value: a string representing the video context * * Helper to set the video context as a string inside the #GstQuery. */ void gst_video_context_query_set_string (GstQuery * query, const gchar * type, const gchar * value) { GstStructure *structure = gst_query_get_structure (query); gst_structure_set (structure, "video-context-type", G_TYPE_STRING, type, "video-context", G_TYPE_STRING, value, NULL); } /** * gst_video_context_query_set_pointer: * @query: a #GstQuery * @type: the video context type * @value: a #gpointer representing the video context * * Helper to set the video context as a #gpointer inside the #GstQuery. */ void gst_video_context_query_set_pointer (GstQuery * query, const gchar * type, gpointer value) { GstStructure *structure = gst_query_get_structure (query); gst_structure_set (structure, "video-context-type", G_TYPE_STRING, type, "video-context", G_TYPE_POINTER, value, NULL); } /** * gst_video_context_query_set_object: * @query: a #GstQuery * @type: the video context type * @value: a #GObject representing the video context * * Helper to set the video context as a #GObject inside the #GstQuery. */ void gst_video_context_query_set_object (GstQuery * query, const gchar * type, GObject * value) { GstStructure *structure = gst_query_get_structure (query); gst_structure_set (structure, "video-context-type", G_TYPE_STRING, type, "video-context", G_TYPE_OBJECT, value, NULL); }