mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 18:51:11 +00:00
540 lines
17 KiB
C
540 lines
17 KiB
C
|
/* GStreamer
|
||
|
*
|
||
|
* Copyright (C) 2011 Intel
|
||
|
* Copyright (C) 2011 Collabora Ltd.
|
||
|
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
|
||
|
*
|
||
|
* 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.
|
||
|
* <note>
|
||
|
* The GstVideoContext interface is unstable API and may change in future.
|
||
|
* One can define GST_USE_UNSTABLE_API to acknowledge and avoid this warning.
|
||
|
* </note>
|
||
|
*
|
||
|
* <refsect2>
|
||
|
* <title>For Element</title>
|
||
|
* <para>
|
||
|
* 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.
|
||
|
* </para>
|
||
|
* <para>
|
||
|
* 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.
|
||
|
* </para>
|
||
|
* <para>
|
||
|
* 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()).
|
||
|
* </para>
|
||
|
* <para>
|
||
|
* 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.
|
||
|
* </para>
|
||
|
* <para>
|
||
|
* 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.
|
||
|
* </para>
|
||
|
* <para>
|
||
|
* 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.
|
||
|
* </para>
|
||
|
* </refsect2>
|
||
|
*
|
||
|
* <refsect2>
|
||
|
* <title>For Application</title>
|
||
|
* <para>
|
||
|
* 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.
|
||
|
* <note>
|
||
|
* 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.
|
||
|
* </note>
|
||
|
* </para>
|
||
|
* <refsect2>
|
||
|
* <title>Example using ClutterVideoGstVideoSink</title>
|
||
|
* <para>
|
||
|
* This example is for user of ClutterGstVideoSink element, the
|
||
|
* ClutterGstPlayer object transparently handle this.
|
||
|
* </para>
|
||
|
* |[
|
||
|
* #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));
|
||
|
*
|
||
|
* ...
|
||
|
* }
|
||
|
* ]|
|
||
|
* </refsect2>
|
||
|
* </refsect2>
|
||
|
*/
|
||
|
|
||
|
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").
|
||
|
* <note>
|
||
|
* Users of X11 Display should ensure that XInitThreads() was called before
|
||
|
* opening the display.
|
||
|
* </note>
|
||
|
*/
|
||
|
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);
|
||
|
}
|