gst_init的过程分析
gstreamer/gst/gst.c
gstreamer在开始的时候,都会调用gst_init初始化gstreamer,gst_init可以处理main函数的argc和argv变量,解析处理自己的option,加载plugin,注册插件等,完成初始化相关工作。
gst_init函数很简单,调用gst_init_check
void
gst_init (int *argc, char **argv[])
{
GError *err = NULL;
if (!gst_init_check (argc, argv, &err)) {
g_print ("Could not initialize GStreamer: %s\n",
err ? err->message : "unknown error occurred");
if (err) {
g_error_free (err);
}
exit (1);
}
}
下面是对gst_init的调用栈展开,逐个分析。
gst_init_check
gstreamer/gst/gst.c
在gst_init_check函数中如果定义了GST_DISABLE_OPTION_PARSING将调用init_post函数,它将设置log callback函数,初始化gst_format,并为gst_object注册一系列类型等等。在init_post函数中还将调用gst_update_registry函数,强制gstreamer对它的plugin所在路径进行重新扫描,并且更新默认的plugin registry。
static gboolean gst_initialized = FALSE;
static gboolean gst_deinitialized = FALSE;
gboolean
gst_init_check (int *argc, char **argv[], GError ** err)
{
static GMutex init_lock;
#ifndef GST_DISABLE_OPTION_PARSING
GOptionGroup *group;
GOptionContext *ctx;
#endif
gboolean res;
g_mutex_lock (&init_lock);
if (gst_initialized) {
GST_DEBUG ("already initialized gst");
g_mutex_unlock (&init_lock);
return TRUE;
}
#ifndef GST_DISABLE_OPTION_PARSING
ctx = g_option_context_new ("- GStreamer initialization");
g_option_context_set_ignore_unknown_options (ctx, TRUE);
g_option_context_set_help_enabled (ctx, FALSE);
group = gst_init_get_option_group ();
g_option_context_add_group (ctx, group);
res = g_option_context_parse (ctx, argc, argv, err);
g_option_context_free (ctx);
#else
init_pre (NULL, NULL, NULL, NULL);
init_post (NULL, NULL, NULL, NULL);
res = TRUE;
#endif
gst_initialized = res;
g_mutex_unlock (&init_lock);
return res;
}
如果没有定义GST_DISABLE_OPTION_PARSING,gst_init_check函数的调用栈如下列表,最后会调用gst_update_registry
- gst_init_get_option_group
- init_pre
- find_executable_path
- _priv_gst_debug_init:GST_DISABLE_GST_DEBUG
- bindtextdomain:ENABLE_NLS
- bind_textdomain_codeset:ENABLE_NLS
- init_post
- g_log_set_handler
- _priv_gst_mini_object_initialize
- _priv_gst_allocator_initialize
- _priv_gst_memory_initialize
- _priv_gst_format_initialize
- _priv_gst_structure_initialize
- _priv_gst_caps_initialize
- _priv_gst_caps_features_initialize
- _priv_gst_meta_initialize
- _priv_gst_message_initialize
- _priv_gst_plugin_initialize
- gst_update_registry
_priv_gst_plugin_initialize可以读取GST_PLUGIN_LOADING_WHITELIST环境变量,提前注册。GST_PLUGIN_LOADING_WHITELIST环境变量可以这么写:
GST_PLUGIN_LOADING_WHITELIST=gstreamer:gst-plugins-base:gst-plugins-good
init_post
如果gst_register_core_elements注册了bin和pipeline插件
gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
"staticelements", "core elements linked into the GStreamer library",
gst_register_core_elements, VERSION, GST_LICENSE, PACKAGE,
GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
init_static_plugins ();
plugin_init
gstplaybackplugin.c (1.19.3版本)
static gboolean
plugin_init (GstPlugin * plugin)
{
gboolean res = FALSE;
if (!g_getenv ("USE_PLAYBIN3"))
res |= GST_ELEMENT_REGISTER (playbin, plugin);
res |= GST_ELEMENT_REGISTER (playbin3, plugin);
res |= GST_ELEMENT_REGISTER (playsink, plugin);
res |= GST_ELEMENT_REGISTER (subtitleoverlay, plugin);
res |= GST_ELEMENT_REGISTER (streamsynchronizer, plugin);
res |= GST_ELEMENT_REGISTER (decodebin, plugin);
res |= GST_ELEMENT_REGISTER (decodebin3, plugin);
res |= GST_ELEMENT_REGISTER (uridecodebin, plugin);
res |= GST_ELEMENT_REGISTER (uridecodebin3, plugin);
res |= GST_ELEMENT_REGISTER (urisourcebin, plugin);
res |= GST_ELEMENT_REGISTER (parsebin, plugin);
return res;
}
playback的plugin_init函数调用栈
gst_plugin_register_func中会调用plugin_init初始化插件。
1 plugin_init gstplaybackplugin.c
2 gst_plugin_register_func gstplugin.c
3 _priv_gst_plugin_load_file_for_registry gstplugin.c
4 gst_plugin_load_file gstplugin.c
5 gst_plugin_load_by_name gstplugin.c
6 gst_plugin_feature_load gstpluginfeature.c
7 gst_element_factory_create_with_properties gstelementfactory.c
8 gst_element_factory_make_with_properties gstelementfactory.c
9 gst_element_factory_make gstelementfactory.c
10 priv_gst_parse_yyparse grammar.y
11 priv_gst_parse_launch grammar.y
12 gst_parse_launch_full gstparse.c
13 gst_parse_launchv_full gstparse.c
14 main gst-launch.c
通过gst-inspect-1.0工具可以看到playback插件包含11个feature,常用的playbin就在其中:
$ gst-inspect-1.0 playback
Plugin Details:
Name playback
Description various playback elements
Filename /usr/local/lib/x86_64-linux-gnu/gstreamer-1.0/libgstplayback.so
Version 1.19.3
License LGPL
Source module gst-plugins-base
Source release date 2021-11-03
Binary package GStreamer Base Plug-ins source release
Origin URL Unknown package origin
decodebin: Decoder Bin
decodebin3: Decoder Bin 3
parsebin: Parse Bin
playbin: Player Bin 2
playbin3: Player Bin 3
playsink: Player Sink
streamsynchronizer: Stream Synchronizer
subtitleoverlay: Subtitle Overlay
uridecodebin: URI Decoder
uridecodebin3: URI Decoder
urisourcebin: URI reader
gst_update_registry
gstreamer/gst/gstregistry.c
在gst_update_registry中调用ensure_current_registry函数,如果_priv_gst_preload_plugins不为NULL,会加载_priv_gst_preload_plugins列表的plugin。
gboolean
gst_update_registry (void)
{
gboolean res;
#ifndef GST_DISABLE_REGISTRY
if (!_priv_gst_disable_registry) {
GError *err = NULL;
res = ensure_current_registry (&err);
if (err) {
GST_WARNING ("registry update failed: %s", err->message);
g_error_free (err);
} else {
GST_LOG ("registry update succeeded");
}
} else {
GST_INFO ("registry update disabled by environment");
res = TRUE;
}
#else
GST_WARNING ("registry update failed: %s", "registry disabled");
res = TRUE;
#endif
if (_priv_gst_preload_plugins) {
GST_DEBUG ("Preloading indicated plugins...");
g_slist_foreach (_priv_gst_preload_plugins, load_plugin_func, NULL);
}
return res;
}
ensure_current_registry
gstreamer/gst/gstplugin.c
ensure_current_registry中会先读取registry文件,ubuntu上默认安装会在home目录下找到registry.x86_64.bin,然后调用scan_and_update_registry进入update registry过程。
static gboolean
ensure_current_registry (GError ** error)
{
gchar *registry_file;
GstRegistry *default_registry;
gboolean ret = TRUE;
gboolean do_update = TRUE;
gboolean have_cache = TRUE;
default_registry = gst_registry_get ();
registry_file = g_strdup (g_getenv ("GST_REGISTRY_1_0"));
if (registry_file == NULL)
registry_file = g_strdup (g_getenv ("GST_REGISTRY"));
if (registry_file == NULL) {
registry_file = g_build_filename (g_get_user_cache_dir (),
"gstreamer-" GST_API_VERSION, GST_REGISTRY_FILE_NAME, NULL);
}
if (!_gst_disable_registry_cache) {
GST_INFO ("reading registry cache: %s", registry_file);
have_cache = priv_gst_registry_binary_read_cache (default_registry,
registry_file);
_gst_disable_registry_cache = TRUE;
}
if (have_cache) {
do_update = !_priv_gst_disable_registry_update;
if (do_update) {
const gchar *update_env;
if ((update_env = g_getenv ("GST_REGISTRY_UPDATE"))) {
do_update = (strcmp (update_env, "no") != 0);
}
}
}
if (do_update) {
const gchar *reuse_env;
if ((reuse_env = g_getenv ("GST_REGISTRY_REUSE_PLUGIN_SCANNER"))) {
__registry_reuse_plugin_scanner = (strcmp (reuse_env, "no") != 0);
}
GST_DEBUG ("Updating registry cache");
scan_and_update_registry (default_registry, registry_file, TRUE, error);
} else {
GST_DEBUG ("Not updating registry cache (disabled)");
}
g_free (registry_file);
GST_INFO ("registry reading and updating done, result = %d", ret);
return ret;
}
scan_and_update_registry
scan_and_update_registry调用栈大致如下,plugin的注册过程有很多细节设计:
scan_and_update_registry
- gst_registry_scan_path_internal
- gst_registry_scan_path_level
- gst_registry_scan_plugin_file
- plugin_loader_load
- gst_plugin_loader_try_helper
- put_packet
- exchange_packets[gst-plugin-scanner]
- read_one
- handle_rx_packet
- do_plugin_load
- gst_plugin_load_file
- _priv_gst_plugin_load_file_for_registry
- extract_symname
- _priv_gst_plugin_load_file_for_registry
- extract_symname
涉及到的环境变量
-
GST_PLUGIN_PATH -
GST_PLUGIN_SYSTEM_PATH
static GstRegistryScanAndUpdateResult
scan_and_update_registry (GstRegistry * default_registry,
const gchar * registry_file, gboolean write_changes, GError ** error)
{
const gchar *plugin_path;
gboolean changed = FALSE;
GList *l;
GstRegistryScanContext context;
GST_INFO ("Validating plugins from registry cache: %s", registry_file);
init_scan_context (&context, default_registry);
GST_DEBUG ("scanning paths added via --gst-plugin-path");
for (l = _priv_gst_plugin_paths; l != NULL; l = l->next) {
GST_INFO ("Scanning plugin path: \"%s\"", (gchar *) l->data);
changed |= gst_registry_scan_path_internal (&context, (gchar *) l->data);
}
plugin_path = g_getenv ("GST_PLUGIN_PATH_1_0");
if (plugin_path == NULL)
plugin_path = g_getenv ("GST_PLUGIN_PATH");
if (plugin_path) {
char **list;
int i;
GST_DEBUG ("GST_PLUGIN_PATH set to %s", plugin_path);
list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; list[i]; i++) {
changed |= gst_registry_scan_path_internal (&context, list[i]);
}
g_strfreev (list);
} else {
GST_DEBUG ("GST_PLUGIN_PATH not set");
}
plugin_path = g_getenv ("GST_PLUGIN_SYSTEM_PATH_1_0");
if (plugin_path == NULL)
plugin_path = g_getenv ("GST_PLUGIN_SYSTEM_PATH");
if (plugin_path == NULL) {
char *home_plugins;
GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH not set");
home_plugins = g_build_filename (g_get_user_data_dir (),
"gstreamer-" GST_API_VERSION, "plugins", NULL);
GST_DEBUG ("scanning home plugins %s", home_plugins);
changed |= gst_registry_scan_path_internal (&context, home_plugins);
g_free (home_plugins);
#ifdef G_OS_WIN32
{
char *base_dir;
char *dir;
base_dir =
g_win32_get_package_installation_directory_of_module
(_priv_gst_dll_handle);
dir = g_build_filename (base_dir, GST_PLUGIN_SUBDIR,
"gstreamer-" GST_API_VERSION, NULL);
GST_DEBUG ("scanning DLL dir %s", dir);
changed |= gst_registry_scan_path_internal (&context, dir);
g_free (dir);
g_free (base_dir);
}
#else
GST_DEBUG ("scanning main plugins %s", PLUGINDIR);
changed |= gst_registry_scan_path_internal (&context, PLUGINDIR);
#endif
} else {
gchar **list;
gint i;
GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH set to %s", plugin_path);
list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; list[i]; i++) {
changed |= gst_registry_scan_path_internal (&context, list[i]);
}
g_strfreev (list);
}
clear_scan_context (&context);
changed |= context.changed;
changed |= gst_registry_remove_cache_plugins (default_registry);
if (!changed) {
GST_INFO ("Registry cache has not changed");
return REGISTRY_SCAN_AND_UPDATE_SUCCESS_NOT_CHANGED;
}
if (!write_changes) {
GST_INFO ("Registry cache changed, but writing is disabled. Not writing.");
return REGISTRY_SCAN_AND_UPDATE_FAILURE;
}
GST_INFO ("Registry cache changed. Writing new registry cache");
if (!priv_gst_registry_binary_write_cache (default_registry,
default_registry->priv->plugins, registry_file)) {
g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
_("Error writing registry cache to %s: %s"),
registry_file, g_strerror (errno));
return REGISTRY_SCAN_AND_UPDATE_FAILURE;
}
GST_INFO ("Registry cache written successfully");
return REGISTRY_SCAN_AND_UPDATE_SUCCESS_UPDATED;
}
scanning output
从log可以看到scan的过程,从相应路径搜索gstreamer的plugin库
scan_and_update_registry: scanning paths added via --gst-plugin-path
scan_and_update_registry: scanning home plugins /home/hui/.local/share/gstreamer-1.0/plugins
gst_registry_scan_path_internal:<registry0> scanning path /home/hui/.local/share/gstreamer-1.0/plugins
scan_and_update_registry: scanning main plugins /usr/lib/x86_64-linux-gnu/gstreamer-1.0
gst_registry_scan_path_internal:<registry0> scanning path /usr/lib/x86_64-linux-gnu/gstreamer-1.0
gst_registry_scan_path_internal
gstreamer/gst/gstregistry.c
比较简单,直接调用gst_registry_scan_path_level函数
static gboolean
gst_registry_scan_path_internal (GstRegistryScanContext * context,
const gchar * path)
{
gboolean changed;
GST_DEBUG_OBJECT (context->registry, "scanning path %s", path);
changed = gst_registry_scan_path_level (context, path, 10);
GST_DEBUG_OBJECT (context->registry, "registry changed in path %s: %d", path,
changed);
return changed;
}
gst_registry_scan_path_level
gstreamer/gst/gstregistry.c
支持目录level,真正的scan实现
打开dir,然后循环读取每一个文件,调用gst_registry_scan_plugin_file去做文件的scan
static gboolean
gst_registry_scan_path_level (GstRegistryScanContext * context,
const gchar * path, int level)
{
GDir *dir;
const gchar *dirent;
gchar *filename;
GstPlugin *plugin;
gboolean changed = FALSE;
dir = g_dir_open (path, 0, NULL);
if (!dir)
return FALSE;
while ((dirent = g_dir_read_name (dir))) {
GStatBuf file_status;
filename = g_build_filename (path, dirent, NULL);
if (g_stat (filename, &file_status) < 0) {
g_free (filename);
continue;
}
if (file_status.st_mode & S_IFDIR) {
if (G_UNLIKELY (is_blacklisted_hidden_directory (dirent))) {
GST_TRACE_OBJECT (context->registry, "ignoring %s directory", dirent);
g_free (filename);
continue;
}
if (level > 0) {
GST_LOG_OBJECT (context->registry, "recursing into directory %s",
filename);
changed |= gst_registry_scan_path_level (context, filename, level - 1);
} else {
GST_LOG_OBJECT (context->registry, "not recursing into directory %s, "
"recursion level too deep", filename);
}
g_free (filename);
continue;
}
if (!(file_status.st_mode & S_IFREG)) {
GST_TRACE_OBJECT (context->registry, "%s is not a regular file, ignoring",
filename);
g_free (filename);
continue;
}
if (!g_str_has_suffix (dirent, "." G_MODULE_SUFFIX)
#ifdef GST_EXTRA_MODULE_SUFFIX
&& !g_str_has_suffix (dirent, GST_EXTRA_MODULE_SUFFIX)
#endif
) {
GST_TRACE_OBJECT (context->registry,
"extension is not recognized as module file, ignoring file %s",
filename);
g_free (filename);
continue;
}
GST_LOG_OBJECT (context->registry, "file %s looks like a possible module",
filename);
if (g_str_has_prefix (dirent, "libgstvalve") ||
g_str_has_prefix (dirent, "libgstselector")) {
GST_WARNING_OBJECT (context->registry, "ignoring old plugin %s which "
"has been merged into the corelements plugin", filename);
g_free (filename);
continue;
}
plugin = gst_registry_lookup_bn (context->registry, dirent);
if (plugin) {
gboolean env_vars_changed, deps_changed = FALSE;
if (plugin->registered) {
GST_DEBUG_OBJECT (context->registry,
"plugin already registered from path \"%s\"",
GST_STR_NULL (plugin->filename));
g_free (filename);
gst_object_unref (plugin);
continue;
}
env_vars_changed = _priv_plugin_deps_env_vars_changed (plugin);
if (plugin->file_mtime == file_status.st_mtime &&
plugin->file_size == file_status.st_size && !env_vars_changed &&
!(deps_changed = _priv_plugin_deps_files_changed (plugin)) &&
!strcmp (plugin->filename, filename)) {
GST_LOG_OBJECT (context->registry, "file %s cached", filename);
GST_OBJECT_FLAG_UNSET (plugin, GST_PLUGIN_FLAG_CACHED);
GST_LOG_OBJECT (context->registry,
"marking plugin %p as registered as %s", plugin, filename);
plugin->registered = TRUE;
} else {
GST_INFO_OBJECT (context->registry, "cached info for %s is stale",
filename);
GST_DEBUG_OBJECT (context->registry, "mtime %" G_GINT64_FORMAT " != %"
G_GINT64_FORMAT " or size %" G_GINT64_FORMAT " != %"
G_GINT64_FORMAT " or external dependency env_vars changed: %d or"
" external dependencies changed: %d or old path %s != new path %s",
(gint64) plugin->file_mtime, (gint64) file_status.st_mtime,
(gint64) plugin->file_size, (gint64) file_status.st_size,
env_vars_changed, deps_changed, plugin->filename, filename);
gst_registry_remove_plugin (context->registry, plugin);
changed |= gst_registry_scan_plugin_file (context, filename,
file_status.st_size, file_status.st_mtime);
}
gst_object_unref (plugin);
} else {
GST_DEBUG_OBJECT (context->registry, "file %s not yet in registry",
filename);
changed |= gst_registry_scan_plugin_file (context, filename,
file_status.st_size, file_status.st_mtime);
}
g_free (filename);
}
g_dir_close (dir);
return changed;
}
gst_registry_scan_plugin_file
gstreamer/gst/gstregistry.c
这个函数里面用到了_priv_gst_plugin_loader_funcs,这个实际上是个struct,包含三个函数
typedef struct _GstPluginLoaderFuncs {
GstPluginLoader * (*create) (GstRegistry *registry);
gboolean (*destroy) (GstPluginLoader *loader);
gboolean (*load) (GstPluginLoader *loader, const gchar *filename,
off_t file_size, time_t file_mtime);
} GstPluginLoaderFuncs;
const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs = {
plugin_loader_new, plugin_loader_free, plugin_loader_load
};
代码中会调用_priv_gst_plugin_loader_funcs.create,create对应的就是plugin_loader_new,_priv_gst_plugin_loader_funcs.load就是plugin_loader_load
static gboolean
gst_registry_scan_plugin_file (GstRegistryScanContext * context,
const gchar * filename, off_t file_size, time_t file_mtime)
{
gboolean changed = FALSE;
GstPlugin *newplugin = NULL;
#ifdef G_OS_WIN32
context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
#endif
if (context->helper_state == REGISTRY_SCAN_HELPER_NOT_STARTED) {
GST_DEBUG ("Starting plugin scanner for file %s", filename);
context->helper = _priv_gst_plugin_loader_funcs.create (context->registry);
if (context->helper != NULL)
context->helper_state = REGISTRY_SCAN_HELPER_RUNNING;
else {
GST_WARNING ("Failed starting plugin scanner. Scanning in-process");
context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
}
}
if (context->helper_state == REGISTRY_SCAN_HELPER_RUNNING) {
GST_DEBUG ("Using scan-helper to load plugin %s", filename);
if (!_priv_gst_plugin_loader_funcs.load (context->helper,
filename, file_size, file_mtime)) {
g_warning ("External plugin loader failed. This most likely means that "
"the plugin loader helper binary was not found or could not be run. "
"You might need to set the GST_PLUGIN_SCANNER environment variable "
"if your setup is unusual. This should normally not be required "
"though.");
context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
}
}
if (context->helper_state == REGISTRY_SCAN_HELPER_DISABLED) {
newplugin = _priv_gst_plugin_load_file_for_registry (filename,
context->registry, NULL);
}
if (newplugin) {
GST_DEBUG_OBJECT (context->registry, "marking new plugin %p as registered",
newplugin);
newplugin->registered = TRUE;
gst_object_unref (newplugin);
changed = TRUE;
}
#ifndef GST_DISABLE_REGISTRY
if (!__registry_reuse_plugin_scanner) {
clear_scan_context (context);
context->helper_state = REGISTRY_SCAN_HELPER_NOT_STARTED;
}
#endif
return changed;
}
plugin_loader_load
gstreamer/gst/gstpluginloader.c
- loader:将会通过它保存plugin的信息
- filename:需要load的库路径
- file_size:库的大小
- file_mtime:库的更新时间
static gboolean
plugin_loader_load (GstPluginLoader * loader, const gchar * filename,
off_t file_size, time_t file_mtime)
{
gint len;
PendingPluginEntry *entry;
if (!gst_plugin_loader_spawn (loader))
return FALSE;
GST_LOG_OBJECT (loader->registry,
"Sending file %s to child. tag %u", filename, loader->next_tag);
entry = g_slice_new (PendingPluginEntry);
entry->tag = loader->next_tag++;
entry->filename = g_strdup (filename);
entry->file_size = file_size;
entry->file_mtime = file_mtime;
loader->pending_plugins_tail =
g_list_append (loader->pending_plugins_tail, entry);
if (loader->pending_plugins == NULL)
loader->pending_plugins = loader->pending_plugins_tail;
else
loader->pending_plugins_tail = g_list_next (loader->pending_plugins_tail);
len = strlen (filename);
put_packet (loader, PACKET_LOAD_PLUGIN, entry->tag,
(guint8 *) filename, len + 1);
if (!exchange_packets (loader)) {
if (!plugin_loader_replay_pending (loader))
return FALSE;
}
return TRUE;
}
_priv_gst_plugin_load_file_for_registry
gstreamer/gst/gstpluginloader.c
load单个文件,如果在gst_registry_scan_plugin_file中scan-helper是disable的,那么plugin_loader_load是不会走的,直接走这个。disable scan-helper就是不走gst-plugin-scanner那部分流程,可以在configure阶段
GstPlugin *
_priv_gst_plugin_load_file_for_registry (const gchar * filename,
GstRegistry * registry, GError ** error)
{
const GstPluginDesc *desc;
GstPlugin *plugin;
gchar *symname;
GModule *module;
gboolean ret;
gpointer ptr;
GStatBuf file_status;
gboolean new_plugin = TRUE;
GModuleFlags flags;
g_return_val_if_fail (filename != NULL, NULL);
if (registry == NULL)
registry = gst_registry_get ();
g_mutex_lock (&gst_plugin_loading_mutex);
plugin = gst_registry_lookup (registry, filename);
if (plugin) {
if (plugin->module) {
g_mutex_unlock (&gst_plugin_loading_mutex);
return plugin;
} else {
new_plugin = FALSE;
}
}
GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "attempt to load plugin \"%s\"",
filename);
if (!g_module_supported ()) {
GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "module loading not supported");
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE, "Dynamic loading not supported");
goto return_error;
}
if (g_stat (filename, &file_status)) {
GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "problem accessing file");
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE, "Problem accessing file %s: %s", filename,
g_strerror (errno));
goto return_error;
}
flags = G_MODULE_BIND_LOCAL;
if (strstr (filename, "libgstpython"))
flags |= G_MODULE_BIND_LAZY;
module = g_module_open (filename, flags);
if (module == NULL) {
GST_CAT_WARNING (GST_CAT_PLUGIN_LOADING, "module_open failed: %s",
g_module_error ());
g_set_error (error,
GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE, "Opening module failed: %s",
g_module_error ());
g_warning ("Failed to load plugin '%s': %s", filename, g_module_error ());
goto return_error;
}
symname = extract_symname (filename);
ret = g_module_symbol (module, symname, &ptr);
if (ret) {
GstPluginDesc *(*get_desc) (void) = ptr;
ptr = get_desc ();
} else {
GST_DEBUG ("Could not find symbol '%s', falling back to gst_plugin_desc",
symname);
ret = g_module_symbol (module, "gst_plugin_desc", &ptr);
}
g_free (symname);
if (!ret) {
GST_DEBUG ("Could not find plugin entry point in \"%s\"", filename);
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE,
"File \"%s\" is not a GStreamer plugin", filename);
g_module_close (module);
goto return_error;
}
desc = (const GstPluginDesc *) ptr;
if (priv_gst_plugin_loading_have_whitelist () &&
!priv_gst_plugin_desc_is_whitelisted (desc, filename)) {
GST_INFO ("Whitelist specified and plugin not in whitelist, not loading: "
"name=%s, package=%s, file=%s", desc->name, desc->source, filename);
g_set_error (error, GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE,
"Not loading plugin file \"%s\", not in whitelist", filename);
g_module_close (module);
goto return_error;
}
if (new_plugin) {
plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
plugin->file_mtime = file_status.st_mtime;
plugin->file_size = file_status.st_size;
plugin->filename = g_strdup (filename);
plugin->basename = g_path_get_basename (filename);
}
plugin->module = module;
if (new_plugin) {
CHECK_PLUGIN_DESC_FIELD (desc, name, filename);
CHECK_PLUGIN_DESC_FIELD (desc, description, filename);
CHECK_PLUGIN_DESC_FIELD (desc, version, filename);
CHECK_PLUGIN_DESC_FIELD (desc, license, filename);
CHECK_PLUGIN_DESC_FIELD (desc, source, filename);
CHECK_PLUGIN_DESC_FIELD (desc, package, filename);
CHECK_PLUGIN_DESC_FIELD (desc, origin, filename);
if (desc->name != NULL && desc->name[0] == '"') {
g_warning ("Invalid plugin name '%s' - fix your GST_PLUGIN_DEFINE "
"(remove quotes around plugin name)", desc->name);
}
if (desc->release_datetime != NULL &&
!check_release_datetime (desc->release_datetime)) {
g_warning ("GstPluginDesc for '%s' has invalid datetime '%s'",
filename, desc->release_datetime);
g_set_error (error, GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE,
"Plugin %s has invalid plugin description field 'release_datetime'",
filename);
goto return_error;
}
}
GST_LOG ("Plugin %p for file \"%s\" prepared, calling entry function...",
plugin, filename);
_gst_plugin_fault_handler_setup ();
_gst_plugin_fault_handler_filename = plugin->filename;
GST_LOG ("Plugin %p for file \"%s\" prepared, registering...",
plugin, filename);
if (!gst_plugin_register_func (plugin, desc, NULL)) {
_gst_plugin_fault_handler_restore ();
GST_DEBUG ("gst_plugin_register_func failed for plugin \"%s\"", filename);
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE,
"File \"%s\" appears to be a GStreamer plugin, but it failed to initialize",
filename);
goto return_error;
}
_gst_plugin_fault_handler_restore ();
_gst_plugin_fault_handler_filename = NULL;
GST_INFO ("plugin \"%s\" loaded", plugin->filename);
if (new_plugin) {
gst_object_ref (plugin);
gst_registry_add_plugin (registry, plugin);
}
g_mutex_unlock (&gst_plugin_loading_mutex);
return plugin;
return_error:
{
if (plugin)
gst_object_unref (plugin);
g_mutex_unlock (&gst_plugin_loading_mutex);
return NULL;
}
}
###### extract_symname
gstreamer/gst/gstplugin.c
生成符号为gst_plugin_xxx_get_desc的字符串
static gchar *
extract_symname (const char *filename)
{
gchar *bname, *name, *symname;
const gchar *dot;
gsize prefix_len, len;
int i;
bname = g_path_get_basename (filename);
for (i = 0; bname[i]; ++i) {
if (bname[i] == '-')
bname[i] = '_';
}
if (g_str_has_prefix (bname, "libgst"))
prefix_len = 6;
else if (g_str_has_prefix (bname, "lib"))
prefix_len = 3;
else if (g_str_has_prefix (bname, "gst"))
prefix_len = 3;
else
prefix_len = 0;
dot = g_utf8_strchr (bname, -1, '.');
if (dot)
len = dot - bname - prefix_len;GST_PLUGIN_DEFINE
else
len = strlen (bname + prefix_len);
name = g_strndup (bname + prefix_len, len);
g_free (bname);
symname = g_strconcat ("gst_plugin_", name, "_get_desc", NULL);
g_free (name);
return symname;
}
gst_plugin_loader_spawn
gstreamer/gst/gstpluginloader.c
获取GST_PLUGIN_SCANNER 环境变量指定的路径,然后运行gst-plugin-scanner,如果返回false,gst-plugin-scanner不可用。
static gboolean
gst_plugin_loader_spawn (GstPluginLoader * loader)
{
const gchar *env;
char *helper_bin;
gboolean res = FALSE;
if (loader->child_running)
return TRUE;
env = g_getenv ("GST_PLUGIN_SCANNER_1_0");
if (env == NULL)
env = g_getenv ("GST_PLUGIN_SCANNER");
if (env != NULL && *env != '\0') {
GST_LOG ("Trying GST_PLUGIN_SCANNER env var: %s", env);
helper_bin = g_strdup (env);
res = gst_plugin_loader_try_helper (loader, helper_bin);
g_free (helper_bin);
} else {
GST_LOG ("Trying installed plugin scanner");
#ifdef G_OS_WIN32
{
gchar *basedir;
basedir =
g_win32_get_package_installation_directory_of_module
(_priv_gst_dll_handle);
helper_bin =
g_build_filename (basedir, GST_PLUGIN_SCANNER_SUBDIR,
"gstreamer-" GST_API_VERSION, "gst-plugin-scanner.exe", NULL);
g_free (basedir);
}
#else
helper_bin = g_strdup (GST_PLUGIN_SCANNER_INSTALLED);
#endif
res = gst_plugin_loader_try_helper (loader, helper_bin);
g_free (helper_bin);
if (!res) {
GST_INFO ("No gst-plugin-scanner available, or not working");
}
}
return loader->child_running;
}
gst_plugin_loader_try_helper
gstreamer/gst/gstpluginloader.c
gst_plugin_loader_try_helpers调用g_spawn_async_with_pipes函数fork出了gst-plugin-scanner进程,最后返回loader->child_running状态。
gst_plugin_loader_try_helpers
- g_spawn_async_with_pipes[glib/gspawn.c]
- fork_exec_with_pipes
static gboolean
gst_plugin_loader_try_helper (GstPluginLoader * loader, gchar * location)
{
char *argv[6] = { NULL, };
int c = 0;
#if defined (__APPLE__) && defined (USR_BIN_ARCH_SWITCH)
if (gst_plugin_loader_use_usr_bin_arch ()) {
argv[c++] = (char *) "/usr/bin/arch";
argv[c++] = (char *) USR_BIN_ARCH_SWITCH;
}
#endif
argv[c++] = location;
argv[c++] = (char *) "-l";
argv[c++] = _gst_executable_path;
argv[c++] = NULL;
if (c > 4) {
GST_LOG ("Trying to spawn gst-plugin-scanner helper at %s with arch %s",
location, argv[1]);
} else {
GST_LOG ("Trying to spawn gst-plugin-scanner helper at %s", location);
}
if (!g_spawn_async_with_pipes (NULL, argv, NULL,
G_SPAWN_DO_NOT_REAP_CHILD ,
NULL, NULL, &loader->child_pid, &loader->fd_w.fd, &loader->fd_r.fd,
NULL, NULL))
return FALSE;
gst_poll_add_fd (loader->fdset, &loader->fd_w);
gst_poll_add_fd (loader->fdset, &loader->fd_r);
gst_poll_fd_ctl_read (loader->fdset, &loader->fd_r, TRUE);
loader->tx_buf_write = loader->tx_buf_read = 0;
put_packet (loader, PACKET_VERSION, 0, NULL, 0);
if (!plugin_loader_sync_with_child (loader))
return FALSE;
loader->child_running = TRUE;
return TRUE;
}
put_packet
gstreamer/gst/gstpluginloader.c
put_packet函数将消息push到管道
static void
put_packet (GstPluginLoader * l, guint type, guint32 tag,
const guint8 * payload, guint32 payload_len)
{
guint8 *out;
guint len = payload_len + HEADER_SIZE;
if (l->tx_buf_write + len >= l->tx_buf_size) {
GST_LOG ("Expanding tx buf from %d to %d for packet of size %d",
l->tx_buf_size, l->tx_buf_write + len + BUF_GROW_EXTRA, len);
l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
}
out = l->tx_buf + l->tx_buf_write;
out[0] = type;
GST_WRITE_UINT24_BE (out + 1, tag);
GST_WRITE_UINT32_BE (out + 4, payload_len);
if (payload && payload_len)
memcpy (out + HEADER_SIZE, payload, payload_len);
GST_WRITE_UINT32_BE (out + 8, HEADER_MAGIC);
l->tx_buf_write += len;
gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
}
plugin_loader_sync_with_child
gstreamer/gst/gstpluginloader.c
plugin_loader_sync_with_child用户和子进程同步
static gboolean
plugin_loader_sync_with_child (GstPluginLoader * l)
{
put_packet (l, PACKET_SYNC, 0, NULL, 0);
l->rx_got_sync = FALSE;
while (!l->rx_got_sync) {
if (!exchange_packets (l))
return FALSE;
}
return TRUE;
}
gst-plugin-scanner子进程
gstreamer/libs/gst/helpers/gst-plugin-scanner.c
在父进程通过管道将PACKET_LOAD_PLUGIN类型的消息到子进程gst-plugin-scanner,而后在gst-plugin-scanner子进程_gst_plugin_loader_client_run () 中循环等待父进程发过来的消息并处理。
int
main (int argc, char *argv[])
{
gboolean res;
char **my_argv;
int my_argc;
if (argc != 2 && argc != 3)
return 1;
if (strcmp (argv[1], "-l"))
return 1;
my_argc = 2;
my_argv = g_malloc (my_argc * sizeof (char *));
my_argv[0] = argv[0];
my_argv[1] = (char *) "--gst-disable-registry-update";
#ifndef GST_DISABLE_REGISTRY
_gst_disable_registry_cache = TRUE;
#endif
if (argc == 3)
_gst_executable_path = g_strdup (argv[2]);
res = gst_init_check (&my_argc, &my_argv, NULL);
g_free (my_argv);
if (!res)
return 1;
if (!_gst_plugin_loader_client_run ())
return 1;
return 0;
}
_gst_plugin_loader_client_run
gstreamer/libs/gst/helpers/gst-plugin-scanner.c
gboolean
_gst_plugin_loader_client_run (void)
{
gboolean res = TRUE;
GstPluginLoader *l;
l = plugin_loader_new (NULL);
if (l == NULL)
return FALSE;
#ifndef G_OS_WIN32
{
int dup_fd;
dup_fd = dup (0);
if (dup_fd == -1) {
GST_ERROR ("Failed to start. Could not dup STDIN, errno %d", errno);
res = FALSE;
goto beach;
}
l->fd_r.fd = dup_fd;
close (0);
dup_fd = dup (1);
if (dup_fd == -1) {
GST_ERROR ("Failed to start. Could not dup STDOUT, errno %d", errno);
res = FALSE;
goto beach;
}
l->fd_w.fd = dup_fd;
close (1);
dup2 (2, 1);
}
#else
l->fd_w.fd = 1;
l->fd_r.fd = 0;
#endif
gst_poll_add_fd (l->fdset, &l->fd_w);
gst_poll_add_fd (l->fdset, &l->fd_r);
gst_poll_fd_ctl_read (l->fdset, &l->fd_r, TRUE);
l->is_child = TRUE;
GST_DEBUG ("Plugin scanner child running. Waiting for instructions");
while (!l->rx_done && exchange_packets (l));
#ifndef G_OS_WIN32
beach:
#endif
plugin_loader_free (l);
return res;
}
exchange_packets
gstreamer/gst/gstpluginloader.c
在exchange_packets()中,将会检查,是否有数据可以进行接收或者发送,如果有,将会进行相应的处理。
假设父进程已经发送PACKET_LOAD_PLUGIN类型的数据过来,接下来,子进程gst-plugin-scanner将会在exchange_packets()函数中,经过一系列的检查之后,通过read_one()函数进行处理:
static gboolean
exchange_packets (GstPluginLoader * l)
{
gint res;
do {
do {
res = gst_poll_wait (l->fdset, GST_SECOND);
} while (res == -1 && (errno == EINTR || errno == EAGAIN));
if (res < 0)
return FALSE;
GST_LOG ("Poll res = %d. %d bytes pending for write", res,
l->tx_buf_write - l->tx_buf_read);
if (!l->rx_done) {
if (gst_poll_fd_has_error (l->fdset, &l->fd_r)) {
GST_LOG ("read fd %d errored", l->fd_r.fd);
goto fail_and_cleanup;
}
if (gst_poll_fd_can_read (l->fdset, &l->fd_r)) {
if (!read_one (l))
goto fail_and_cleanup;
} else if (gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
GST_LOG ("read fd %d closed", l->fd_r.fd);
goto fail_and_cleanup;
}
}
if (l->tx_buf_read < l->tx_buf_write) {
if (gst_poll_fd_has_error (l->fdset, &l->fd_w)) {
GST_ERROR ("write fd %d errored", l->fd_w.fd);
goto fail_and_cleanup;
}
if (gst_poll_fd_can_write (l->fdset, &l->fd_w)) {
if (!write_one (l))
goto fail_and_cleanup;
} else if (gst_poll_fd_has_closed (l->fdset, &l->fd_w)) {
GST_LOG ("write fd %d closed", l->fd_w.fd);
goto fail_and_cleanup;
}
}
} while (l->tx_buf_read < l->tx_buf_write);
return TRUE;
fail_and_cleanup:
plugin_loader_cleanup_child (l);
return FALSE;
}
read_one
gstreamer/gst/gstpluginloader.c
在read_one()函数中,按照协商好的协议,读取packet,再通过handle_rx_packet函数处理
static gboolean
read_one (GstPluginLoader * l)
{
guint64 magic;
guint32 to_read, packet_len, tag;
guint8 *in;
gint res;
to_read = HEADER_SIZE;
in = l->rx_buf;
do {
res = read (l->fd_r.fd, in, to_read);
if (G_UNLIKELY (res < 0)) {
if (errno == EAGAIN || errno == EINTR)
continue;
GST_LOG ("Failed reading packet header");
return FALSE;
}
to_read -= res;
in += res;
} while (to_read > 0);
magic = GST_READ_UINT32_BE (l->rx_buf + 8);
if (magic != HEADER_MAGIC) {
GST_WARNING
("Invalid packet (bad magic number) received from plugin scanner subprocess");
return FALSE;
}
packet_len = GST_READ_UINT32_BE (l->rx_buf + 4);
if (packet_len + HEADER_SIZE > BUF_MAX_SIZE) {
GST_WARNING
("Received excessively large packet for plugin scanner subprocess");
return FALSE;
}
tag = GST_READ_UINT24_BE (l->rx_buf + 1);
if (packet_len > 0) {
if (packet_len + HEADER_SIZE >= l->rx_buf_size) {
GST_LOG ("Expanding rx buf from %d to %d",
l->rx_buf_size, packet_len + HEADER_SIZE + BUF_GROW_EXTRA);
l->rx_buf_size = packet_len + HEADER_SIZE + BUF_GROW_EXTRA;
l->rx_buf = g_realloc (l->rx_buf, l->rx_buf_size);
}
in = l->rx_buf + HEADER_SIZE;
to_read = packet_len;
do {
res = read (l->fd_r.fd, in, to_read);
if (G_UNLIKELY (res < 0)) {
if (errno == EAGAIN || errno == EINTR)
continue;
GST_ERROR ("Packet payload read failed");
return FALSE;
}
to_read -= res;
in += res;
} while (to_read > 0);
} else {
GST_LOG ("No payload to read for 0 length packet type %d tag %u",
l->rx_buf[0], tag);
}
return handle_rx_packet (l, l->rx_buf[0], tag,
l->rx_buf + HEADER_SIZE, packet_len);
}
handle_rx_packet
gstreamer/gst/gstpluginloader.c
在handle_rx_packet()函数中,根据pack_type的类型,即l->rx_buf[0]的值进行相应的操作,比如加载plugin是PACKET_LOAD_PLUGIN,将会调用do_plugin_load()函数进行处理。
static gboolean
handle_rx_packet (GstPluginLoader * l,
guint pack_type, guint32 tag, guint8 * payload, guint payload_len)
{
gboolean res = TRUE;
switch (pack_type) {
case PACKET_EXIT:
gst_poll_fd_ctl_read (l->fdset, &l->fd_r, FALSE);
if (l->is_child) {
put_packet (l, PACKET_EXIT, 0, NULL, 0);
}
l->rx_done = TRUE;
return TRUE;
case PACKET_LOAD_PLUGIN:{
if (!l->is_child)
return TRUE;
res = do_plugin_load (l, (gchar *) payload, tag);
break;
}
case PACKET_PLUGIN_DETAILS:{
gchar *tmp = (gchar *) payload;
PendingPluginEntry *entry = NULL;
GList *cur;
GST_DEBUG_OBJECT (l->registry,
"Received plugin details from child w/ tag %u. %d bytes info",
tag, payload_len);
cur = l->pending_plugins;
while (cur) {
PendingPluginEntry *e = (PendingPluginEntry *) (cur->data);
if (e->tag > tag)
break;
if (e->tag == tag) {
entry = e;
break;
} else {
cur = g_list_delete_link (cur, cur);
g_free (e->filename);
g_slice_free (PendingPluginEntry, e);
}
}
l->pending_plugins = cur;
if (cur == NULL)
l->pending_plugins_tail = NULL;
if (payload_len > 0) {
GstPlugin *newplugin = NULL;
if (!_priv_gst_registry_chunks_load_plugin (l->registry, &tmp,
tmp + payload_len, &newplugin)) {
GST_ERROR_OBJECT (l->registry,
"Problems loading plugin details with tag %u from scanner", tag);
return FALSE;
}
GST_OBJECT_FLAG_UNSET (newplugin, GST_PLUGIN_FLAG_CACHED);
GST_LOG_OBJECT (l->registry,
"marking plugin %p as registered as %s", newplugin,
newplugin->filename);
newplugin->registered = TRUE;
l->got_plugin_details = TRUE;
} else if (entry != NULL) {
plugin_loader_create_blacklist_plugin (l, entry);
l->got_plugin_details = TRUE;
}
if (entry != NULL) {
g_free (entry->filename);
g_slice_free (PendingPluginEntry, entry);
}
cur = l->pending_plugins;
if (cur != NULL)
cur = g_list_delete_link (cur, cur);
l->pending_plugins = cur;
if (cur == NULL)
l->pending_plugins_tail = NULL;
break;
}
case PACKET_SYNC:
if (l->is_child) {
put_packet (l, PACKET_SYNC, tag, NULL, 0);
GST_LOG ("Got SYNC in child - replying");
} else
l->rx_got_sync = TRUE;
break;
case PACKET_VERSION:
if (l->is_child) {
const gint version_len =
sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN;
guint8 version_info[sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN];
memset (version_info, 0, version_len);
GST_WRITE_UINT32_BE (version_info, loader_protocol_version);
memcpy (version_info + sizeof (guint32), GST_MAGIC_BINARY_VERSION_STR,
strlen (GST_MAGIC_BINARY_VERSION_STR));
put_packet (l, PACKET_VERSION, tag, version_info, version_len);
GST_LOG ("Got VERSION in child - replying %u", loader_protocol_version);
} else {
res = check_protocol_version (l, payload, payload_len);
}
break;
default:
return FALSE;
}
return res;
}
do_plugin_load
gstreamer/gst/gstpluginloader.c
在do_plugin_load()函数中,先通过gst_plugin_load_file()加载plugin并将相应信息反馈父进程。
子进程gst-plugin-scanner将会把plugin的外部依赖、支持的features、以及element desc等信息反馈到父进程
static gboolean
do_plugin_load (GstPluginLoader * l, const gchar * filename, guint tag)
{
GstPlugin *newplugin;
GList *chunks = NULL;
GST_DEBUG ("Plugin scanner loading file %s. tag %u", filename, tag);
newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
if (newplugin) {
guint hdr_pos;
guint offset;
if (!_priv_gst_registry_chunks_save_plugin (&chunks,
gst_registry_get (), newplugin))
goto fail;
hdr_pos = l->tx_buf_write;
offset = HEADER_SIZE;
put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
if (chunks) {
GList *walk;
for (walk = chunks; walk; walk = g_list_next (walk)) {
GstRegistryChunk *cur = walk->data;
put_chunk (l, cur, &offset);
_priv_gst_registry_chunk_free (cur);
}
g_list_free (chunks);
GST_WRITE_UINT32_BE (l->tx_buf + hdr_pos + 4, offset - HEADER_SIZE);
}
gst_object_unref (newplugin);
} else {
put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
}
return TRUE;
fail:
put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
if (chunks) {
GList *walk;
for (walk = chunks; walk; walk = g_list_next (walk)) {
GstRegistryChunk *cur = walk->data;
_priv_gst_registry_chunk_free (cur);
}
g_list_free (chunks);
}
return FALSE;
}
gst_plugin_load_file
gstreamer/gst/gstplugin.c
gst_plugin_load_file()函数也是通过_priv_gst_plugin_load_file_for_registry()函数完成插件信息的获取
GstPlugin *
gst_plugin_load_file (const gchar * filename, GError ** error)
{
return _priv_gst_plugin_load_file_for_registry (filename, NULL, error);
}
_priv_gst_plugin_load_file_for_registry
和前面的_priv_gst_plugin_load_file_for_registry标题是同一个函数
GstPlugin *
_priv_gst_plugin_load_file_for_registry (const gchar * filename,
GstRegistry * registry, GError ** error)
{
const GstPluginDesc *desc;
GstPlugin *plugin;
gchar *symname;
GModule *module;
gboolean ret;
gpointer ptr;
GStatBuf file_status;
gboolean new_plugin = TRUE;
GModuleFlags flags;
g_return_val_if_fail (filename != NULL, NULL);
if (registry == NULL)
registry = gst_registry_get ();
g_mutex_lock (&gst_plugin_loading_mutex);
plugin = gst_registry_lookup (registry, filename);
if (plugin) {
if (plugin->module) {
g_mutex_unlock (&gst_plugin_loading_mutex);
return plugin;
} else {
new_plugin = FALSE;
}
}
GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "attempt to load plugin \"%s\"",
filename);
if (!g_module_supported ()) {
GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "module loading not supported");
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE, "Dynamic loading not supported");
goto return_error;
}
if (g_stat (filename, &file_status)) {
GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "problem accessing file");
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE, "Problem accessing file %s: %s", filename,
g_strerror (errno));
goto return_error;
}
flags = G_MODULE_BIND_LOCAL;
if (strstr (filename, "libgstpython"))
flags |= G_MODULE_BIND_LAZY;
module = g_module_open (filename, flags);
if (module == NULL) {
GST_CAT_WARNING (GST_CAT_PLUGIN_LOADING, "module_open failed: %s",
g_module_error ());
g_set_error (error,
GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE, "Opening module failed: %s",
g_module_error ());
g_warning ("Failed to load plugin '%s': %s", filename, g_module_error ());
goto return_error;
}
symname = extract_symname (filename);
ret = g_module_symbol (module, symname, &ptr);
if (ret) {
GstPluginDesc *(*get_desc) (void) = ptr;
ptr = get_desc ();
} else {
GST_DEBUG ("Could not find symbol '%s', falling back to gst_plugin_desc",
symname);
ret = g_module_symbol (module, "gst_plugin_desc", &ptr);
}
g_free (symname);
if (!ret) {
GST_DEBUG ("Could not find plugin entry point in \"%s\"", filename);
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE,
"File \"%s\" is not a GStreamer plugin", filename);
g_module_close (module);
goto return_error;
}
desc = (const GstPluginDesc *) ptr;
if (priv_gst_plugin_loading_have_whitelist () &&
!priv_gst_plugin_desc_is_whitelisted (desc, filename)) {
GST_INFO ("Whitelist specified and plugin not in whitelist, not loading: "
"name=%s, package=%s, file=%s", desc->name, desc->source, filename);
g_set_error (error, GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE,
"Not loading plugin file \"%s\", not in whitelist", filename);
g_module_close (module);
goto return_error;
}
if (new_plugin) {
plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
plugin->file_mtime = file_status.st_mtime;
plugin->file_size = file_status.st_size;
plugin->filename = g_strdup (filename);
plugin->basename = g_path_get_basename (filename);
}
plugin->module = module;
if (new_plugin) {
CHECK_PLUGIN_DESC_FIELD (desc, name, filename);
CHECK_PLUGIN_DESC_FIELD (desc, description, filename);
CHECK_PLUGIN_DESC_FIELD (desc, version, filename);
CHECK_PLUGIN_DESC_FIELD (desc, license, filename);
CHECK_PLUGIN_DESC_FIELD (desc, source, filename);
CHECK_PLUGIN_DESC_FIELD (desc, package, filename);
CHECK_PLUGIN_DESC_FIELD (desc, origin, filename);
if (desc->name != NULL && desc->name[0] == '"') {
g_warning ("Invalid plugin name '%s' - fix your GST_PLUGIN_DEFINE "
"(remove quotes around plugin name)", desc->name);
}
if (desc->release_datetime != NULL &&
!check_release_datetime (desc->release_datetime)) {
g_warning ("GstPluginDesc for '%s' has invalid datetime '%s'",
filename, desc->release_datetime);
g_set_error (error, GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE,
"Plugin %s has invalid plugin description field 'release_datetime'",
filename);
goto return_error;
}
}
GST_LOG ("Plugin %p for file \"%s\" prepared, calling entry function...",
plugin, filename);
_gst_plugin_fault_handler_setup ();
_gst_plugin_fault_handler_filename = plugin->filename;
GST_LOG ("Plugin %p for file \"%s\" prepared, registering...",
plugin, filename);
if (!gst_plugin_register_func (plugin, desc, NULL)) {
_gst_plugin_fault_handler_restore ();
GST_DEBUG ("gst_plugin_register_func failed for plugin \"%s\"", filename);
g_set_error (error,
GST_PLUGIN_ERROR,
GST_PLUGIN_ERROR_MODULE,
"File \"%s\" appears to be a GStreamer plugin, but it failed to initialize",
filename);
goto return_error;
}
_gst_plugin_fault_handler_restore ();
_gst_plugin_fault_handler_filename = NULL;
GST_INFO ("plugin \"%s\" loaded", plugin->filename);
if (new_plugin) {
gst_object_ref (plugin);
gst_registry_add_plugin (registry, plugin);
}
g_mutex_unlock (&gst_plugin_loading_mutex);
return plugin;
return_error:
{
if (plugin)
gst_object_unref (plugin);
g_mutex_unlock (&gst_plugin_loading_mutex);
return NULL;
}
}
gst_plugin_register_func
gstreamer/gst/gstplugin.c
gst_plugin_register_func函数里将调用进入每一个插件的入口函数plugin_init。每一个plugin,都需要在c文件中定义plugin_init函数和宏GST_PLUGIN_DEFINE,宏GST_PLUGIN_DEFINE用于定义一个plugin的入口点和元数据。
static GstPlugin *
gst_plugin_register_func (GstPlugin * plugin, const GstPluginDesc * desc,
gpointer user_data)
{
if (!gst_plugin_check_version (desc->major_version, desc->minor_version)) {
if (GST_CAT_DEFAULT)
GST_WARNING ("plugin \"%s\" has incompatible version "
"(plugin: %d.%d, gst: %d,%d), not loading",
GST_STR_NULL (plugin->filename), desc->major_version,
desc->minor_version, GST_VERSION_MAJOR, GST_VERSION_MINOR);
return NULL;
}
if (!desc->license || !desc->description || !desc->source ||
!desc->package || !desc->origin) {
if (GST_CAT_DEFAULT)
GST_WARNING ("plugin \"%s\" has missing detail in GstPluginDesc, not "
"loading", GST_STR_NULL (plugin->filename));
return NULL;
}
if (!gst_plugin_check_license (desc->license)) {
if (GST_CAT_DEFAULT)
GST_WARNING ("plugin \"%s\" has invalid license \"%s\", not loading",
GST_STR_NULL (plugin->filename), desc->license);
return NULL;
}
if (GST_CAT_DEFAULT)
GST_LOG ("plugin \"%s\" looks good", GST_STR_NULL (plugin->filename));
gst_plugin_desc_copy (&plugin->desc, desc);
if (plugin->module)
g_module_make_resident (plugin->module);
if (user_data) {
if (!(((GstPluginInitFullFunc) (desc->plugin_init)) (plugin, user_data))) {
if (GST_CAT_DEFAULT)
GST_WARNING ("plugin \"%s\" failed to initialise",
GST_STR_NULL (plugin->filename));
return NULL;
}
} else {
if (!((desc->plugin_init) (plugin))) {
if (GST_CAT_DEFAULT)
GST_WARNING ("plugin \"%s\" failed to initialise",
GST_STR_NULL (plugin->filename));
return NULL;
}
}
if (GST_CAT_DEFAULT)
GST_LOG ("plugin \"%s\" initialised", GST_STR_NULL (plugin->filename));
return plugin;
}
gst_registry_add_plugin
gst_registry_add_plugin()函数则是将plugin保存到_gst_registry_default的priv->plugins成员
gboolean
gst_registry_add_plugin (GstRegistry * registry, GstPlugin * plugin)
{
GstPlugin *existing_plugin;
g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
GST_OBJECT_LOCK (registry);
if (G_LIKELY (plugin->basename)) {
existing_plugin =
gst_registry_lookup_bn_locked (registry, plugin->basename);
if (existing_plugin) {
GST_DEBUG_OBJECT (registry,
"Replacing existing plugin \"%s\" %p with new plugin %p for filename \"%s\"",
GST_STR_NULL (existing_plugin->filename), existing_plugin, plugin,
GST_STR_NULL (plugin->filename));
if (GST_OBJECT_FLAG_IS_SET (plugin, GST_PLUGIN_FLAG_BLACKLISTED) &&
strcmp (plugin->filename, existing_plugin->filename)) {
GST_WARNING_OBJECT (registry,
"Not replacing plugin because new one (%s) is blacklisted but for a different location than existing one (%s)",
plugin->filename, existing_plugin->filename);
gst_object_ref_sink (plugin);
gst_object_unref (plugin);
GST_OBJECT_UNLOCK (registry);
return FALSE;
}
registry->priv->plugins =
g_list_remove (registry->priv->plugins, existing_plugin);
--registry->priv->n_plugins;
if (G_LIKELY (existing_plugin->basename))
g_hash_table_remove (registry->priv->basename_hash,
existing_plugin->basename);
gst_object_unref (existing_plugin);
}
}
GST_DEBUG_OBJECT (registry, "adding plugin %p for filename \"%s\"",
plugin, GST_STR_NULL (plugin->filename));
registry->priv->plugins = g_list_prepend (registry->priv->plugins, plugin);
++registry->priv->n_plugins;
if (G_LIKELY (plugin->basename))
g_hash_table_replace (registry->priv->basename_hash, plugin->basename,
plugin);
gst_object_ref_sink (plugin);
GST_OBJECT_UNLOCK (registry);
GST_LOG_OBJECT (registry, "emitting plugin-added for filename \"%s\"",
GST_STR_NULL (plugin->filename));
g_signal_emit (registry, gst_registry_signals[PLUGIN_ADDED], 0, plugin);
return TRUE;
}
gst_init_get_option_group
gstreamer/gst/gst.c
GOptionGroup *
gst_init_get_option_group (void)
{
#ifndef GST_DISABLE_OPTION_PARSING
GOptionGroup *group;
static const GOptionEntry gst_args[] = {
{"gst-version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg, N_("Print the GStreamer version"), NULL},
{"gst-fatal-warnings", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg, N_("Make all warnings fatal"), NULL},
#ifndef GST_DISABLE_GST_DEBUG
{"gst-debug-help", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Print available debug categories and exit"),
NULL},
{"gst-debug-level", 0, 0, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Default debug level from 1 (only error) to 9 (anything) or "
"0 for no output"),
N_("LEVEL")},
{"gst-debug", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) parse_goption_arg,
N_("Comma-separated list of category_name:level pairs to set "
"specific levels for the individual categories. Example: "
"GST_AUTOPLUG:5,GST_ELEMENT_*:3"),
N_("LIST")},
{"gst-debug-no-color", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg, N_("Disable colored debugging output"),
NULL},
{"gst-debug-color-mode", 0, 0, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Changes coloring mode of the debug log. "
"Possible modes: off, on, disable, auto, unix"),
NULL},
{"gst-debug-disable", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg, N_("Disable debugging"), NULL},
#endif
{"gst-plugin-spew", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Enable verbose plugin loading diagnostics"),
NULL},
{"gst-plugin-path", 0, 0, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Colon-separated paths containing plugins"), N_("PATHS")},
{"gst-plugin-load", 0, 0, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Comma-separated list of plugins to preload in addition to the "
"list stored in environment variable GST_PLUGIN_PATH"),
N_("PLUGINS")},
{"gst-disable-segtrap", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Disable trapping of segmentation faults during plugin loading"),
NULL},
{"gst-disable-registry-update", 0, G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Disable updating the registry"),
NULL},
{"gst-disable-registry-fork", 0, G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK,
(gpointer) parse_goption_arg,
N_("Disable spawning a helper process while scanning the registry"),
NULL},
{NULL}
};
gst_init流程图
Created with Rapha?l 2.3.0
gst_init
gst_init_check
init_post
gst_update_registry
ensure_current_registry
priv_gst_registry_binary_read_cache
have_cache-update?
scan_and_update_registry
gst_registry_scan_path_internal
gst_registry_scan_path_level
gst_registry_scan_plugin_file
gst-plugin-scanner
plugin_loader_load
put_packet
gst-plugin-scanner
exchange_packets
read_one
handle_rx_packet
do_plugin_load
gst_plugin_load_file
_priv_gst_plugin_load_file_for_registry
gst_plugin_register_func
gst_registry_add_plugin
End
yes
no
yes
no
参考
plugin注册流程分析
GSTREAMER学习笔记:PLUGIN注册流程分析
|