gstreamer插件scan过程

gstreamer插件scan过程


gstreamer plugin注册(加载)的过程,是从机器端的相应路径搜索gstreamer的plugin库,同时将相应的plugin信息保存下来,方便后续在查找使用的时候,能够加快这一个过程。

gstreamer将plugin的库都放到同一个路径下,比如/usr/lib/gstreamer-1.0,将无plugin信息的库,放到/usr/lib/目录下,经过这样的方式,减小搜索的范围。同时,gstreamer还经过建立辅助子进程的方式,让子进程来帮忙获取plugin的信息。

这个就要以plugin_loader_load() 函数为入口,通过分析plugin_loader_load的load过程,以及和scanner子进程的交互,来理解scan的过程。


plugin_loader_load说明

plugin_loader_load负责创建gst-plugin-scanner子进程,并且向scanner进程下发扫描插件的指令,并且通过socket和scanner进程通讯,完成插件的更新。

static gboolean
plugin_loader_load (GstPluginLoader * loader, const gchar * filename,
    off_t file_size, time_t file_mtime)

函数也就四个参数,一个是loader,保存plugin的信息,filename则是库的路径,file_size是库的大小,file_mtime则是库的更新时间。在plugin_loader_load()函数中,将会经过gst_plugin_loader_spawn (loader)函数建立子进程:

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

  /* 发送一个PACKET_LOAD_PLUGIN消息到子进程 */
  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;
}
  • 检查环境变量GST_PLUGIN_SCANNER_1_0以及GST_PLUGIN_SCANNER,检测是否有指定的gst-plugin-scanner
  • 若是没有,将会检查有没有GST_PLUGIN_SCANNER_INSTALLED
  • 获取到gst-plugin-scanner后,经过fork()建立子进程运行gst-plugin-scanner,父进程将从plugin的库路径搜索库,并检查有效性,而子进程则是负责从相应的库中获取plugin信息并返回。

plugin_loader_load的流程

顺便看一下进入plugin_loader_load的流程,最后再看gst-plugin-scanner,plugin_loader_load的是在_priv_gst_plugin_loader_funcs中指定的:

/* functions used in GstRegistry scanning */
const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs = {
  plugin_loader_new, plugin_loader_free, plugin_loader_load
};

然后在gst_registry_scan_plugin_file中根据helper_state调用load函数:

  /* 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);
    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);
    
    /* 调用plugin_loader_load,如果返回false,说明辅助程序没有找到,helper状态就设置为disabled */
    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;
    }
  }

如果helper_state是disabled状态,就不会fork子进程,以老式方式加载插件,即在同一个进程中扫描加载插件。

上面这段代码是在gst_registry_scan_plugin_file中调用的,存在下面的调用栈(从下往上),init_post中调用gst_update_registry更新registry,ensure_current_registry中获取GST_REGISTRY指定的cache文件,如果没有cache文件,直接进行do_update操作,进入scan_and_update_registry。如果有cache文件,do_update还依赖于GST_DISABLE_REGISTRYGST_REGISTRY_UPDATE的值。

1 gst_registry_scan_plugin_file   gstregistry.c 1167
2 gst_registry_scan_path_level    gstregistry.c 1357
3 gst_registry_scan_path_internal gstregistry.c 1384
4 scan_and_update_registry        gstregistry.c 1679
5 ensure_current_registry         gstregistry.c 1813
6 gst_update_registry             gstregistry.c 1890
7 init_post                       gst.c         830

ensure_current_registry

ensure_current_registry中调用scan_and_update_registry启动scan和update:

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

然后,在scan_and_update_registry先通过init_scan_context初始化registry,最后调用gst_registry_scan_path_internal扫描GST_PLUGIN_PATH指定路径中的插件库。

init_scan_context

init_scan_context中获取GST_REGISTRY_FORK环境变量,确定scanner helper_state的状态:

static void
init_scan_context (GstRegistryScanContext * context, GstRegistry * registry)
{
  gboolean do_fork;

  context->registry = registry;

  /* see if forking is enabled and set up the scan helper state accordingly */
  do_fork = _gst_enable_registry_fork;
  if (do_fork) {
    const gchar *fork_env;

    /* forking enabled, see if it is disabled with an env var */
    if ((fork_env = g_getenv ("GST_REGISTRY_FORK"))) {
      /* fork enabled for any value different from "no" */
      do_fork = strcmp (fork_env, "no") != 0;
    }
  }

  if (do_fork)
    context->helper_state = REGISTRY_SCAN_HELPER_NOT_STARTED;
  else
    context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;

  context->helper = NULL;
  context->changed = FALSE;
}

gst_registry_scan_path_level

gst_registry_scan_path_level中会根据文件的状态去判断是否发生了改变,是个递归调用。

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

最后在scan_and_update_registry中根据change状态,决定是否需要更新registry文件:

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

这部分是在plugin_loader_load之前,和gst-plugin-scanner相关的还是要回到前面的plugin_loader_load部分。


子进程gst-plugin-scanner

gst-plugin-scanner子进程在前面说过,在plugin_loader_load中fork出来,处理父进程交给它的scan任务,最后返回plugin的信息给父进程。

subprojects/gstreamer/libs/gst/helpers/gst-plugin-scanner.c


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;

  /* 在这个run函数内部循环等待父进程发过来的消息并处理 */
  /* Create registry scanner listener and run */
  if (!_gst_plugin_loader_client_run ())
    return 1;

  return 0;
}

_gst_plugin_loader_client_run ()函数中设置好管道的接收和发送端口以后,经过如下循环进行接收处理:

  /* Loop, listening for incoming packets on the fd and writing responses */
  while (!l->rx_done && exchange_packets (l));

在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);
  // ...
}

而在read_one()函数中,按照协商好的协议,读取解析rx_buf,再经过如下handle_rx_packet函数处理收到的数据rx_buf:

TX and RX are abbreviations for Transmit and Receive

  • rx:receive
  • tx:transmit
  return handle_rx_packet (l, l->rx_buf[0], tag,
      l->rx_buf + HEADER_SIZE, packet_len);

这里l是GstPluginLoader对象,l->rx_buf[0]的值就是pack_type的类型,pack_type是PACKET_LOAD_PLUGIN,在handle_rx_packet中,根据PACKET_LOAD_PLUGIN,调用do_plugin_load()函数进行处理:

    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;

在do_plugin_load()函数中,先经过gst_plugin_load_file()加载plugin并将相应信息反馈父进程:

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;

        /* 发送PACKET_PLUGIN_DETAILS类型消息到父进程 */
        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;
                
                /* 发送external deps、plugin features、element desc等信息 */
                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;
    // ..
}

scan相关的环境变量和option


REGISTRY和PLUGIN相关的环境变量

GST_REGISTRY
GST_REGISTRY_UPDATE
GST_REGISTRY_FORK
GST_PLUGIN_PATH
GST_PLUGIN_SCANNER
GST_PLUGIN_SCANNER_1_0
GST_DISABLE_REGISTRY_DEFINE

option

–gst-disable-registry-fork

可以通过--gst-disable-registry-fork禁用fork

subprojects/gstreamer/gst/gst.c

parse_goption_arg:

    {
    "--gst-plugin-spew", ARG_PLUGIN_SPEW}, {
    "--gst-plugin-path", ARG_PLUGIN_PATH}, {
    "--gst-plugin-load", ARG_PLUGIN_LOAD}, {
    "--gst-disable-segtrap", ARG_SEGTRAP_DISABLE}, {
    "--gst-disable-registry-update", ARG_REGISTRY_UPDATE_DISABLE}, {
    "--gst-disable-registry-fork", ARG_REGISTRY_FORK_DISABLE}, {
    NULL}
  };

parse_one_option:

    case ARG_REGISTRY_FORK_DISABLE:
      gst_registry_fork_set_enabled (FALSE);

–gst-plugin-path

等同于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;

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值