gstreamer初始化和plugin registry过程

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);

  // gst_initialized是一个static全局变量
  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);

  // 初始化option
  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插件

  /* register core plugins */
  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);

  // 在libgstreamer与一些插件静态链接的情况下,调用libgstreamer-full-1.0
  // 中定义的gst_init_static_plugins()
  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;

  // 如果ENABLE了REGISTRY
#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 /* GST_DISABLE_REGISTRY */

  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);
  }

  // ubuntu上的log输出如下,registry.x86_64.bin是生成的registry文件:
  // reading registry cache: /home/hui/.cache/gstreamer-1.0/registry.x86_64.bin

  if (!_gst_disable_registry_cache) {
    GST_INFO ("reading registry cache: %s", registry_file);

    //读cache
    have_cache = priv_gst_registry_binary_read_cache (default_registry,
        registry_file);
    /* Only ever read the registry cache once, then disable it for
     * subsequent updates during the program lifetime */
    _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 for any value different from "no" */
        do_update = (strcmp (update_env, "no") != 0);
      }
    }
  }

  if (do_update) {
    const gchar *reuse_env;

    if ((reuse_env = g_getenv ("GST_REGISTRY_REUSE_PLUGIN_SCANNER"))) {
      /* do reuse for any value different from "no" */
      __registry_reuse_plugin_scanner = (strcmp (reuse_env, "no") != 0);
    }
    /* now check registry */
    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);

  /* It sounds tempting to just compare the mtime of directories with the mtime
   * of the registry cache, but it does not work. It would not catch updated
   * plugins, which might bring more or less features.
   */

  // --gst-plugin-path选项指定scan对应的目录
  /* scan paths specified via --gst-plugin-path */
  
  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);
  }
  /* keep plugin_paths around in case a re-scan is forced later on */

  
  // scan GST_PLUGIN_PATH环境变量指定的目录
    
  /* GST_PLUGIN_PATH specifies a list of directories to scan for
   * additional plugins.  These take precedence over the system plugins */
  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");
  }

  // scan GST_PLUGIN_SYSTEM_PATH环境变量指定的目录
    
  /* GST_PLUGIN_SYSTEM_PATH specifies a list of plugins that are always
   * loaded by default.  If not set, this defaults to the system-installed
   * path, and the plugins installed in the user's home directory */
  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");

    /* plugins in the user's home directory take precedence over
     * system-installed ones */
    home_plugins = g_build_filename (g_get_user_data_dir (),
        "gstreamer-" GST_API_VERSION, "plugins", NULL);

    // 输出scanning home log,在后面的log部分可以看到
      
    // gst_registry_scan_path_internal还会多出一条log
    // /home/hui/.local/share/gstreamer-1.0/plugins
    GST_DEBUG ("scanning home plugins %s", home_plugins);
      
    changed |= gst_registry_scan_path_internal (&context, home_plugins);
    g_free (home_plugins);

    /* add the main (installed) library path */

#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
    // 主要路径,ubuntu上是/usr/lib/x86_64-linux-gnu/gstreamer-1.0
    GST_DEBUG ("scanning main plugins %s", PLUGINDIR);
   
    // scan path调用
    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;

  /* Remove cached plugins so stale info is cleared. */
  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;
  }

  // 写register文件
  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;

  // 循环load每一个文件
  while ((dirent = g_dir_read_name (dir))) {
    GStatBuf file_status;

    filename = g_build_filename (path, dirent, NULL);
    if (g_stat (filename, &file_status) < 0) {
      /* Plugin will be removed from cache after the scan completes if it
       * is still marked 'cached' */
      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;
      }
      /* FIXME 2.0: Don't recurse into directories, this behaviour
       * is inconsistent with other PATH environment variables
       */
      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;
    }

    // 忽略regular文件
    if (!(file_status.st_mode & S_IFREG)) {
      GST_TRACE_OBJECT (context->registry, "%s is not a regular file, ignoring",
          filename);
      g_free (filename);
      continue;
    }
    // 忽略extension不是module文件能识别的文件
    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);

    // 忽略旧的不再用的plugin名称
    /* try to avoid unnecessary plugin-move pain */
    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);
      /* Plugin will be removed from cache after the scan completes if it
       * is still marked 'cached' */
      g_free (filename);
      continue;
    }

    /* plug-ins are considered unique by basename; if the given name
     * was already seen by the registry, we ignore it */
    plugin = gst_registry_lookup_bn (context->registry, dirent);
    if (plugin) {
      gboolean env_vars_changed, deps_changed = FALSE;

      // 忽略已经在registry存在的plugin
      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 a file with a certain basename is seen on a different path,
       * update the plugin to ensure the registry cache will reflect up
       * to date information */

      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;

/* functions used in GstRegistry scanning */
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
  /* Disable external plugin loader on Windows until it is ported properly. */
  context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
#endif


  /* Have a plugin to load - see if the scan-helper needs starting */
  if (context->helper_state == REGISTRY_SCAN_HELPER_NOT_STARTED) {
    GST_DEBUG ("Starting plugin scanner for file %s", filename);
    
    // 调用_priv_gst_plugin_loader_funcs.create
    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);
    
    // 调用_priv_gst_plugin_loader_funcs.load
    // 就是plugin_loader_load
    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;
    }
  }

  // 如果scaner-help被disable了,则不会走plugin_loader_load
  /* Check if the helper is disabled (or just got disabled above) */
  if (context->helper_state == REGISTRY_SCAN_HELPER_DISABLED) {
    /* Load plugin the old fashioned way... */

    /* We don't use a GError here because a failure to load some shared
     * objects as plugins is normal (particularly in the uninstalled case)
     */
    // 前面的load失败后走这个,直接load文件
    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;

  // 创建子进程gst-plugin-scanner来工作
  // ubuntu: /usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner
  if (!gst_plugin_loader_spawn (loader))
    return FALSE;

  /* Send a packet to the child requesting that it load the given file */
  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);

  // push消息PACKET_LOAD_PLUGIN到子进程pipe
  len = strlen (filename);
  put_packet (loader, PACKET_LOAD_PLUGIN, entry->tag,
      (guint8 *) filename, len + 1);

  // 通过exchange_packets()函数处理消息
  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);

  // 查到registry里面有这个plugin,new_plugin修改为false
  plugin = gst_registry_lookup (registry, filename);
  if (plugin) {
    if (plugin->module) {
      /* already loaded */
      g_mutex_unlock (&gst_plugin_loading_mutex);
      return plugin;
    } else {
      /* load plugin and update fields */
      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;
  /* libgstpython.so is the gst-python plugin loader. It needs to be loaded with
   * G_MODULE_BIND_LAZY.
   *
   * Ideally there should be a generic way for plugins to specify that they
   * need to be loaded with _LAZY.
   * */
  if (strstr (filename, "libgstpython"))
    flags |= G_MODULE_BIND_LAZY;

  // 打开module库文件
  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 ());
    /* If we failed to open the shared object, then it's probably because a
     * plugin is linked against the wrong libraries. Print out an easy-to-see
     * message in this case. */
    g_warning ("Failed to load plugin '%s': %s", filename, g_module_error ());
    goto return_error;
  }

  // 这两行代码会拼一个symname为gst_plugin_namexxx__get_desc的符号名
  // 这个符号名称就是通过GST_PLUGIN_DEFINE生成的,在gst-avdemux部分分析过
  symname = extract_symname (filename);
  ret = g_module_symbol (module, symname, &ptr);

  if (ret) {
    // 然后调用gst_plugin_namexxx__get_desc
    GstPluginDesc *(*get_desc) (void) = ptr;
    ptr = get_desc ();
  } else {
    GST_DEBUG ("Could not find symbol '%s', falling back to gst_plugin_desc",
        symname);
    // else,直接获取gst_plugin_desc符号,也是通过GST_PLUGIN_DEFINE生成
    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;

  // 根据desc判断如果没有在白名单里面,不loading
  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;
  }

  // 生成new plugin
  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) {
    // desc就是从前面extract_symname得来的
    /* check plugin description: complain about bad values and fail */
    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);

  /* this is where we load the actual .so, so let's trap SIGSEGV */
  _gst_plugin_fault_handler_setup ();
  _gst_plugin_fault_handler_filename = plugin->filename;

  GST_LOG ("Plugin %p for file \"%s\" prepared, registering...",
      plugin, filename);

  // 注册plugin
  // TODO:这个地方调用register和GST_PLUGIN_DEFINE展开调用registry什么区别?
  if (!gst_plugin_register_func (plugin, desc, NULL)) {
    /* remove signal handler */
    _gst_plugin_fault_handler_restore ();
    GST_DEBUG ("gst_plugin_register_func failed for plugin \"%s\"", filename);
    /* plugin == NULL */
    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;
  }

  /* remove signal handler */
  _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;             /* use whole name (minus suffix) as plugin name */

  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);

  // 生成符号为gst_plugin_namexxx__get_desc的字符串
  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;

  /* Find the gst-plugin-scanner: first try the env-var if it is set,
   * otherwise use the installed version */
  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 /* | G_SPAWN_STDERR_TO_DEV_NULL */ ,
          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;

  /* one byte packet type */
  out[0] = type;
  /* 3 byte packet tag number */
  GST_WRITE_UINT24_BE (out + 1, tag);
  /* 4 bytes packet length */
  GST_WRITE_UINT32_BE (out + 4, payload_len);
  /* payload */
  if (payload && payload_len)
    memcpy (out + HEADER_SIZE, payload, payload_len);
  /* Write magic into the header */
  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;

  /* We may or may not have an executable path */
  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;

  // 在_gst_plugin_loader_client_run中循环等待父进程发过来的消息并处理
  // 在_gst_plugin_loader_client_run中调用exchange_packets
  /* Create registry scanner listener and run */
  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;

  /* On entry, the inward pipe is STDIN, and outward is STDOUT.
   * Dup those somewhere better so that plugins printing things
   * won't interfere with anything */
#ifndef G_OS_WIN32
  {
    int dup_fd;

    dup_fd = dup (0);           /* STDIN */
    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);           /* STDOUT */
    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);

    /* Dup stderr down to stdout so things that plugins print are visible,
     * but don't care if it fails */
    dup2 (2, 1);
  }
#else
  /* FIXME: Use DuplicateHandle and friends on win32 */
  l->fd_w.fd = 1;               /* STDOUT */
  l->fd_r.fd = 0;               /* STDIN */
#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");

  // 通过以下循环进行接收处理操作
  /* Loop, listening for incoming packets on the fd and writing responses */
  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;

  /* Wait for activity on our FDs */
  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)) {
        // 调用read_one
        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);
  }

  // 在handle_rx_packet()函数中,根据pack_type的类型,即l->rx_buf[0]的值进行相应的操作
  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) {
        /* Respond */
        put_packet (l, PACKET_EXIT, 0, NULL, 0);
      }
      l->rx_done = TRUE;
      return TRUE;
    case PACKET_LOAD_PLUGIN:{
      if (!l->is_child)
        return TRUE;

      /* Payload is the filename to load */
      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);

      /* Assume that tagged details come back in the order
       * we requested, and delete anything before (but not
       * including) this one */
      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)) {
          /* Got garbage from the child, so fail and trigger replay of plugins */
          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;

        /* We got a set of plugin details - remember it for later */
        l->got_plugin_details = TRUE;
      } else if (entry != NULL) {
        /* Create a blacklist entry for this file to prevent scanning every time */
        plugin_loader_create_blacklist_plugin (l, entry);
        l->got_plugin_details = TRUE;
      }

      if (entry != NULL) {
        g_free (entry->filename);
        g_slice_free (PendingPluginEntry, entry);
      }

      /* Remove the plugin entry we just loaded */
      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) {
        /* Respond with our reply - also a sync */
        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) {
        /* Respond with our reply - a version packet, with the version */
        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;             /* Invalid packet -> something is wrong */
  }

  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);

  // 通过库的文件路径,搜索库并得到相应的plugin数据
  newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
  if (newplugin) {
    guint hdr_pos;
    guint offset;

    // 将plugin信息保存到chunks
    /* Now serialise the plugin details and send */
    if (!_priv_gst_registry_chunks_save_plugin (&chunks,
            gst_registry_get (), newplugin))
      goto fail;

    /* Store where the header is, write an empty one, then write
     * all the payload chunks, then fix up the header size */
    hdr_pos = l->tx_buf_write;
    offset = HEADER_SIZE;
    put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);

    if (chunks) {
      GList *walk;
      // 发送external deps、plugin features、element desc等信息
      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);

      /* Store the size of the written payload */
      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);

  // 在registry中检查,该路径的库是否已经注册,以及会进行一系列的文件检查操作
  plugin = gst_registry_lookup (registry, filename);
  if (plugin) {
    if (plugin->module) {
      /* already loaded */
      g_mutex_unlock (&gst_plugin_loading_mutex);
      return plugin;
    } else {
      /* load plugin and update fields */
      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;
  /* libgstpython.so is the gst-python plugin loader. It needs to be loaded with
   * G_MODULE_BIND_LAZY.
   *
   * Ideally there should be a generic way for plugins to specify that they
   * need to be loaded with _LAZY.
   * */
  if (strstr (filename, "libgstpython"))
    flags |= G_MODULE_BIND_LAZY;

  // 通过g_module_open打开库并获取相应的句柄
  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 ());
    /* If we failed to open the shared object, then it's probably because a
     * plugin is linked against the wrong libraries. Print out an easy-to-see
     * message in this case. */
    g_warning ("Failed to load plugin '%s': %s", filename, g_module_error ());
    goto return_error;
  }

  // 通过extract_symname()函数获取plugin的gst_plugin_name_get_desc函数名称
  // 然后通过g_module_symbol()函数调用dlsym()从库中获取函数地址
  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 description: complain about bad values and fail */
    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);

  /* this is where we load the actual .so, so let's trap SIGSEGV */
  _gst_plugin_fault_handler_setup ();
  _gst_plugin_fault_handler_filename = plugin->filename;

  GST_LOG ("Plugin %p for file \"%s\" prepared, registering...",
      plugin, filename);

  // 注册plugin
  if (!gst_plugin_register_func (plugin, desc, NULL)) {
    /* remove signal handler */
    _gst_plugin_fault_handler_restore ();
    GST_DEBUG ("gst_plugin_register_func failed for plugin \"%s\"", filename);
    /* plugin == NULL */
    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;
  }

  /* remove signal handler */
  _gst_plugin_fault_handler_restore ();
  _gst_plugin_fault_handler_filename = NULL;
  GST_INFO ("plugin \"%s\" loaded", plugin->filename);

  // add plugin
  if (new_plugin) {
    gst_object_ref (plugin);
    // gst_registry_add_plugin()函数则是将plugin保存到_gst_registry_default的priv->plugins成员
    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);

  /* make resident so we're really sure it never gets unloaded again.
   * Theoretically this is not needed, but practically it doesn't hurt.
   * And we're rather safe than sorry. */
  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)) {
    /* we have a basename, see if we find the plugin */
    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 the new plugin is blacklisted and the existing one isn't cached, do not
       * accept if it's from a different location than the existing one */
      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);
        /* Keep reference counting consistent */
        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注册流程分析

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值