主页 > 其他  > 

playbin之Source插件加载流程源码剖析

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://", " ", "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

标签:

playbin之Source插件加载流程源码剖析由讯客互联其他栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“playbin之Source插件加载流程源码剖析