playbin之Source插件加载流程源码剖析
之前我们有讲解过uridecodebin的setup_source中会创建source插件,关键函数:
/* create and configure an element that can handle the uri */
source = gen_source_element (decoder);
/*
* Generate and configure a source element.
*
* Returns: (transfer full): a new #GstElement
*/
static GstElement *
gen_source_element (GstURIDecodeBin * decoder)
{
GObjectClass *source_class;
GstElement *source;
GParamSpec *pspec;
GstQuery *query;
GstSchedulingFlags flags;
GError *err = NULL;
if (!decoder->uri)
goto no_uri;
GST_LOG_OBJECT (decoder, "finding source for %s", decoder->uri);
if (!gst_uri_is_valid (decoder->uri))
goto invalid_uri;
if (IS_BLACKLISTED_URI (decoder->uri))
goto uri_blacklisted;
source =
gst_element_make_from_uri (GST_URI_SRC, decoder->uri, "source", &err);
if (!source)
goto no_source;
GST_LOG_OBJECT (decoder, "found source type %s", G_OBJECT_TYPE_NAME (source));
source_class = G_OBJECT_GET_CLASS (source);
pspec = g_object_class_find_property (source_class, "connection-speed");
if (pspec != NULL) {
guint64 speed = decoder->connection_speed / 1000;
gboolean wrong_type = FALSE;
if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_UINT) {
GParamSpecUInt *pspecuint = G_PARAM_SPEC_UINT (pspec);
speed = CLAMP (speed, pspecuint->minimum, pspecuint->maximum);
} else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_INT) {
GParamSpecInt *pspecint = G_PARAM_SPEC_INT (pspec);
speed = CLAMP (speed, pspecint->minimum, pspecint->maximum);
} else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_UINT64) {
GParamSpecUInt64 *pspecuint = G_PARAM_SPEC_UINT64 (pspec);
speed = CLAMP (speed, pspecuint->minimum, pspecuint->maximum);
} else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_INT64) {
GParamSpecInt64 *pspecint = G_PARAM_SPEC_INT64 (pspec);
speed = CLAMP (speed, pspecint->minimum, pspecint->maximum);
} else {
GST_WARNING_OBJECT (decoder,
"The connection speed property %" G_GUINT64_FORMAT
" of type %s is not useful not setting it", speed,
g_type_name (G_PARAM_SPEC_TYPE (pspec)));
wrong_type = TRUE;
}
if (!wrong_type) {
g_object_set (source, "connection-speed", speed, NULL);
GST_DEBUG_OBJECT (decoder,
"setting connection-speed=%" G_GUINT64_FORMAT " to source element",
speed);
}
}
pspec = g_object_class_find_property (source_class, "subtitle-encoding");
if (pspec != NULL && G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_STRING) {
GST_DEBUG_OBJECT (decoder,
"setting subtitle-encoding=%s to source element", decoder->encoding);
g_object_set (source, "subtitle-encoding", decoder->encoding, NULL);
}
/* Sink reference before passing it to signal handler.
* Language binding might otherwise sink it and then unref.
* A floating ref is also tricky for a native signal handler in case
* of a "transfer floating" call followed by unref
* (e.g. some container_add and then container_remove).
* Bottom line; best not have a floating ref boldly going into unknown code.
*/
g_object_ref_sink (source);
g_signal_emit (decoder, gst_uri_decode_bin_signals[SIGNAL_SOURCE_SETUP],
0, source);
decoder->is_stream = IS_STREAM_URI (decoder->uri);
query = gst_query_new_scheduling ();
if (gst_element_query (source, query)) {
gst_query_parse_scheduling (query, &flags, NULL, NULL, NULL);
if ((flags & GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED))
decoder->is_stream = TRUE;
}
gst_query_unref (query);
GST_LOG_OBJECT (decoder, "source is stream: %d", decoder->is_stream);
decoder->need_queue = IS_QUEUE_URI (decoder->uri);
GST_LOG_OBJECT (decoder, "source needs queue: %d", decoder->need_queue);
return source;
/* ERRORS */
no_uri:
{
GST_ELEMENT_ERROR (decoder, RESOURCE, NOT_FOUND,
(_("No URI specified to play from.")), (NULL));
return NULL;
}
invalid_uri:
{
GST_ELEMENT_ERROR (decoder, RESOURCE, NOT_FOUND,
(_("Invalid URI \"%s\"."), decoder->uri), (NULL));
g_clear_error (&err);
return NULL;
}
uri_blacklisted:
{
GST_ELEMENT_ERROR (decoder, RESOURCE, FAILED,
(_("This stream type cannot be played yet.")), (NULL));
return NULL;
}
no_source:
{
/* whoops, could not create the source element, dig a little deeper to
* figure out what might be wrong. */
if (err != NULL && err->code == GST_URI_ERROR_UNSUPPORTED_PROTOCOL) {
gchar *prot;
prot = gst_uri_get_protocol (decoder->uri);
if (prot == NULL)
goto invalid_uri;
gst_element_post_message (GST_ELEMENT_CAST (decoder),
gst_missing_uri_source_message_new (GST_ELEMENT (decoder), prot));
GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN,
(_("No URI handler implemented for \"%s\"."), prot), (NULL));
g_free (prot);
} else {
GST_ELEMENT_ERROR (decoder, RESOURCE, NOT_FOUND,
("%s", (err) ? err->message : "URI was not accepted by any element"),
("No element accepted URI '%s'", decoder->uri));
}
g_clear_error (&err);
return NULL;
}
}
关键函数:
(1) gst_uri_is_valid (decoder->uri) 对uri进行校验
static void
gst_uri_protocol_check_internal (const gchar * uri, gchar ** endptr)
{
gchar *check = (gchar *) uri;
g_assert (uri != NULL);
g_assert (endptr != NULL);
if (g_ascii_isalpha (*check)) {
check++;
while (g_ascii_isalnum (*check) || *check == '+'
|| *check == '-' || *check == '.')
check++;
}
*endptr = check;
}
(2)IS_BLACKLISTED_URI (decoder->uri)黑名单校验
/* blacklisted URIs, we know they will always fail. */
static const gchar *blacklisted_uris[] = { NULL };
(3)decoder->is_stream = IS_STREAM_URI (decoder->uri);
/* list of URIs that we consider to be streams and that need buffering.
* We have no mechanism yet to figure this out with a query. */
static const gchar *stream_uris[] = { "http://", "https://", "mms://",
"mmsh://", "mmsu://", "mmst://", "fd://", "myth://", "ssh://",
"ftp://", "sftp://",
NULL
};
(4)decoder->need_queue = IS_QUEUE_URI (decoder->uri);
/* list of URIs that need a queue because they are pretty bursty */
static const gchar *queue_uris[] = { "cdda://", NULL };
最关键函数:
source = gst_element_make_from_uri (GST_URI_SRC, decoder->uri, "source", &err);
/**
* gst_element_make_from_uri:
* @type: Whether to create a source or a sink
* @uri: URI to create an element for
* @elementname: (allow-none): Name of created element, can be %NULL.
* @error: (allow-none): address where to store error information, or %NULL.
*
* Creates an element for handling the given URI.
*
* Returns: (transfer floating): a new element or %NULL if none
* could be created
*/
GstElement *
gst_element_make_from_uri (const GstURIType type, const gchar * uri,
const gchar * elementname, GError ** error)
{
GList *possibilities, *walk;
gchar *protocol;
GstElement *ret = NULL;
g_return_val_if_fail (gst_is_initialized (), NULL);
g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (!gst_uri_is_valid (uri)) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
_("Invalid URI: %s"), uri);
return NULL;
}
GST_DEBUG ("type:%d, uri:%s, elementname:%s", type, uri, elementname);
protocol = gst_uri_get_protocol (uri);
possibilities = get_element_factories_from_uri_protocol (type, protocol);
if (!possibilities) {
GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source",
uri);
/* The error message isn't great, but we don't expect applications to
* show that error to users, but call the missing plugins functions */
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_UNSUPPORTED_PROTOCOL,
_("No URI handler for the %s protocol found"), protocol);
g_free (protocol);
return NULL;
}
g_free (protocol);
possibilities = g_list_sort (possibilities, (GCompareFunc) sort_by_rank);
walk = possibilities;
while (walk) {
GstElementFactory *factory = walk->data;
GError *uri_err = NULL;
ret = gst_element_factory_create (factory, elementname);
if (ret != NULL) {
GstURIHandler *handler = GST_URI_HANDLER (ret);
if (gst_uri_handler_set_uri (handler, uri, &uri_err))
break;
GST_WARNING ("%s didn't accept URI '%s': %s", GST_OBJECT_NAME (ret), uri,
uri_err->message);
if (error != NULL && *error == NULL)
g_propagate_error (error, uri_err);
else
g_error_free (uri_err);
gst_object_unref (ret);
ret = NULL;
}
walk = walk->next;
}
gst_plugin_feature_list_free (possibilities);
GST_LOG_OBJECT (ret, "created %s for URL '%s'",
type == GST_URI_SINK ? "sink" : "source", uri);
/* if the first handler didn't work, but we found another one that works */
if (ret != NULL)
g_clear_error (error);
return ret;
}
(1)protocol = gst_uri_get_protocol (uri) 获取协议,例如http/https/ftp等
(2)possibilities = get_element_factories_from_uri_protocol (type, protocol),从注册表中搜索可匹配上的source的lists
类型:GST_URI_SRC 协议:protocols
static GList *
get_element_factories_from_uri_protocol (const GstURIType type,
const gchar * protocol)
{
GList *possibilities;
SearchEntry entry;
g_return_val_if_fail (protocol, NULL);
entry.type = type;
entry.protocol = protocol;
possibilities = gst_registry_feature_filter (gst_registry_get (),
search_by_entry, FALSE, &entry);
return possibilities;
}
(3)对possibilities排序,从中选择最终匹配的source
possibilities = g_list_sort (possibilities, (GCompareFunc) sort_by_rank);
walk = possibilities;
while (walk) {
GstElementFactory *factory = walk->data;
GError *uri_err = NULL;
ret = gst_element_factory_create (factory, elementname);
if (ret != NULL) {
GstURIHandler *handler = GST_URI_HANDLER (ret);
if (gst_uri_handler_set_uri (handler, uri, &uri_err))
break;
GST_WARNING ("%s didn't accept URI '%s': %s", GST_OBJECT_NAME (ret), uri,
uri_err->message);
if (error != NULL && *error == NULL)
g_propagate_error (error, uri_err);
else
g_error_free (uri_err);
gst_object_unref (ret);
ret = NULL;
}
walk = walk->next;
}
a)根据rank排序
b) 对每一个source尝试gst_uri_handler_set_uri ,返回成功则选择该source插件
ret = gst_element_factory_create (factory, elementname);
if (ret != NULL) {
GstURIHandler *handler = GST_URI_HANDLER (ret);
if (gst_uri_handler_set_uri (handler, uri, &uri_err))
break;
c) gst_uri_handler_set_uri主要是从,soruce中获取接口iface = GST_URI_HANDLER_GET_INTERFACE (handler),对比source支持的get_protocols是否与当前uri匹配,匹配则成功。
/**
* gst_uri_handler_set_uri:
* @handler: A #GstURIHandler
* @uri: URI to set
* @error: (allow-none): address where to store a #GError in case of
* an error, or %NULL
*
* Tries to set the URI of the given handler.
*
* Returns: %TRUE if the URI was set successfully, else %FALSE.
*/
gboolean
gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri,
GError ** error)
{
GstURIHandlerInterface *iface;
gboolean ret;
gchar *protocol;
g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
iface = GST_URI_HANDLER_GET_INTERFACE (handler);
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (iface->set_uri != NULL, FALSE);
if (!gst_uri_is_valid (uri)) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
_("Invalid URI: %s"), uri);
return FALSE;
}
protocol = gst_uri_get_protocol (uri);
if (iface->get_protocols) {
const gchar *const *protocols;
const gchar *const *p;
gboolean found_protocol = FALSE;
protocols = iface->get_protocols (G_OBJECT_TYPE (handler));
if (protocols != NULL) {
for (p = protocols; *p != NULL; ++p) {
if (g_ascii_strcasecmp (protocol, *p) == 0) {
found_protocol = TRUE;
break;
}
}
if (!found_protocol) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_UNSUPPORTED_PROTOCOL,
_("URI scheme '%s' not supported"), protocol);
g_free (protocol);
return FALSE;
}
}
}
ret = iface->set_uri (handler, uri, error);
g_free (protocol);
return ret;
}
备注:
Source插件需要实现接口GstURIHandlerInterface
/**
* GstURIHandlerInterface:
* @parent: The parent interface type
* @get_type: Method to tell whether the element handles source or sink URI.
* @get_protocols: Method to return the list of protocols handled by the element.
* @get_uri: Method to return the URI currently handled by the element.
* @set_uri: Method to set a new URI.
*
* Any #GstElement using this interface should implement these methods.
*/
struct _GstURIHandlerInterface {
GTypeInterface parent;
/* vtable */
/*< public >*/
/* querying capabilities */
GstURIType (* get_type) (GType type);
const gchar * const * (* get_protocols) (GType type);
/* using the interface */
gchar * (* get_uri) (GstURIHandler * handler);
gboolean (* set_uri) (GstURIHandler * handler,
const gchar * uri,
GError ** error);
};
souphttpsrc的例子:
G_DEFINE_TYPE_WITH_CODE (GstSoupHTTPSrc, gst_soup_http_src, GST_TYPE_PUSH_SRC,
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
gst_soup_http_src_uri_handler_init));
static void
gst_soup_http_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_soup_http_src_uri_get_type;
iface->get_protocols = gst_soup_http_src_uri_get_protocols;
iface->get_uri = gst_soup_http_src_uri_get_uri;
iface->set_uri = gst_soup_http_src_uri_set_uri;
}
(6)那我们继续看下gst_element_make_from_uri中的关键步骤 possibilities = get_element_factories_from_uri_protocol (type, protocol)->gst_registry_feature_filter
possibilities = gst_registry_feature_filter (gst_registry_get (),
search_by_entry, FALSE, &entry);
主要逻辑就是从注册表中匹配对应的source:
static gboolean
search_by_entry (GstPluginFeature * feature, gpointer search_entry)
{
const gchar *const *protocols;
GstElementFactory *factory;
SearchEntry *entry = (SearchEntry *) search_entry;
if (!GST_IS_ELEMENT_FACTORY (feature))
return FALSE;
factory = GST_ELEMENT_FACTORY_CAST (feature);
if (factory->uri_type != entry->type)
return FALSE;
protocols = gst_element_factory_get_uri_protocols (factory);
if (protocols == NULL) {
g_warning ("Factory '%s' implements GstUriHandler interface but returned "
"no supported protocols!", gst_plugin_feature_get_name (feature));
return FALSE;
}
while (*protocols != NULL) {
if (g_ascii_strcasecmp (*protocols, entry->protocol) == 0)
return TRUE;
protocols++;
}
return FALSE;
}
/**
* gst_element_factory_get_uri_protocols:
* @factory: a #GstElementFactory
*
* Gets a %NULL-terminated array of protocols this element supports or %NULL if
* no protocols are supported. You may not change the contents of the returned
* array, as it is still owned by the element factory. Use g_strdupv() to
* make a copy of the protocol string array if you need to.
*
* Returns: (transfer none) (array zero-terminated=1): the supported protocols
* or %NULL
*/
const gchar *const *
gst_element_factory_get_uri_protocols (GstElementFactory * factory)
{
g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), NULL);
return (const gchar * const *) factory->uri_protocols;
}
看下几个常见的source插件的信息:
gst-inspect-1.0 soupsrc
Factory Details:
Rank primary (256)
Long-name HTTP client source
Klass Source/Network
Description Receive data as a client over the network via HTTP using SOUP
Author Wouter Cloetens <wouter@mind.be>
Plugin Details:
Name soup
Description libsoup HTTP client src/sink
Filename /libgstsoup.so
Version 1.20
License LGPL
Source module gst-plugins-good
Binary package GStreamer Good Plug-ins git
Origin URL Unknown package origin
GObject
+----GInitiallyUnowned
+----GstObject
+----GstElement
+----GstBaseSrc
+----GstPushSrc
+----GstSoupHTTPSrc
Implemented Interfaces:
GstURIHandler
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
ANY
Element has no clocking capabilities.
URI handling capabilities:
Element can act as source.
Supported URI protocols:
http
https
icy
icyx
Pads:
SRC: 'src'
Pad Template: 'src'
Element Properties:
automatic-redirect : Automatically follow HTTP redirects (HTTP Status Code 3xx)
flags: readable, writable
Boolean. Default: true
blocksize : Size in bytes to read per buffer (-1 = default)
flags: readable, writable
Unsigned Integer. Range: 0 - 4294967295 Default: 4096
compress : Allow compressed content encodings
flags: readable, writable
Boolean. Default: false
cookies : HTTP request cookies
flags: readable, writable
Boxed pointer of type "GStrv"
do-timestamp : Apply current stream time to buffers
flags: readable, writable
Boolean. Default: false
extra-headers : Extra headers to append to the HTTP request
flags: readable, writable
Boxed pointer of type "GstStructure"
http-log-level : Set log level for soup's HTTP session log
flags: readable, writable
Enum "SoupLoggerLogLevel" Default: 2, "headers"
(0): none - SOUP_LOGGER_LOG_NONE
(1): minimal - SOUP_LOGGER_LOG_MINIMAL
(2): headers - SOUP_LOGGER_LOG_HEADERS
(3): body - SOUP_LOGGER_LOG_BODY
iradio-mode : Enable internet radio mode (ask server to send shoutcast/icecast metadata interleaved with the actual stream data)
flags: readable, writable
Boolean. Default: true
is-live : Act like a live source
flags: readable, writable
Boolean. Default: false
keep-alive : Use HTTP persistent connections
flags: readable, writable
Boolean. Default: true
location : Location to read from
flags: readable, writable
String. Default: null
method : The HTTP method to use (GET, HEAD, OPTIONS, etc)
flags: readable, writable
String. Default: null
name : The name of the object
flags: readable, writable, 0x2000
String. Default: "souphttpsrc0"
num-buffers : Number of buffers to output before sending EOS (-1 = unlimited)
flags: readable, writable
Integer. Range: -1 - 2147483647 Default: -1
parent : The parent of the object
flags: readable, writable, 0x2000
Object of type "GstObject"
proxy : HTTP proxy server URI
flags: readable, writable
String. Default: ""
proxy-id : HTTP proxy URI user id for authentication
flags: readable, writable
String. Default: null
proxy-pw : HTTP proxy URI user password for authentication
flags: readable, writable
String. Default: null
retries : Maximum number of retries until giving up (-1=infinite)
flags: readable, writable
Integer. Range: -1 - 2147483647 Default: 3
ssl-ca-file : Location of a SSL anchor CA file to use
flags: readable, writable
String. Default: null
ssl-strict : Strict SSL certificate checking
flags: readable, writable
Boolean. Default: true
ssl-use-system-ca-file: Use system CA file
flags: readable, writable
Boolean. Default: true
timeout : Value in seconds to timeout a blocking I/O (0 = No timeout).
flags: readable, writable
Unsigned Integer. Range: 0 - 3600 Default: 15
tls-database : TLS database with anchor certificate authorities used to validate the server certificate
flags: readable, writable
Object of type "GTlsDatabase"
tls-interaction : A GTlsInteraction object to be used when the connection or certificate database need to interact with the user.
flags: readable, writable
Object of type "GTlsInteraction"
typefind : Run typefind before negotiating (deprecated, non-functional)
flags: readable, writable, deprecated
Boolean. Default: false
user-agent : Value of the User-Agent HTTP request header field
flags: readable, writable
String. Default: "GStreamer souphttpsrc 1.20.6.1 "
user-id : HTTP location URI user id for authentication
flags: readable, writable
String. Default: null
user-pw : HTTP location URI user password for authentication
flags: readable, writable
String. Default: null
gst-inspect-1.0 filesrc
Factory Details:
Rank primary (256)
Long-name File Source
Klass Source/File
Description Read from arbitrary point in a file
Author Erik Walthinsen <omega@cse.ogi.edu>
Plugin Details:
Name coreelements
Description GStreamer core elements
Filename /libgstcoreelements.so
Version 1.20
License LGPL
Source module gstreamer
Binary package GStreamer git
Origin URL Unknown package origin
GObject
+----GInitiallyUnowned
+----GstObject
+----GstElement
+----GstBaseSrc
+----GstFileSrc
Implemented Interfaces:
GstURIHandler
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
ANY
Element has no clocking capabilities.
URI handling capabilities:
Element can act as source.
Supported URI protocols:
file
Pads:
SRC: 'src'
Pad Template: 'src'
Element Properties:
blocksize : Size in bytes to read per buffer (-1 = default)
flags: readable, writable
Unsigned Integer. Range: 0 - 4294967295 Default: 4096
do-timestamp : Apply current stream time to buffers
flags: readable, writable
location : Location of the file to read
flags: readable, writable, changeable only in NULL or READY state
String. Default: null
name : The name of the object
flags: readable, writable, 0x2000
String. Default: "filesrc0"
num-buffers : Number of buffers to output before sending EOS (-1 = unlimited)
flags: readable, writable
Integer. Range: -1 - 2147483647 Default: -1
parent : The parent of the object
flags: readable, writable, 0x2000
Object of type "GstObject"
typefind : Run typefind before negotiating (deprecated, non-functional)
flags: readable, writable, deprecated
Boolean. Default: false