gstreamer初始化和plugin registry过程_spawning gst-plugin-scanner helper failed failed (1)


既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
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


**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**
**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**
G, 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
[外链图片转存中…(img-kBrRPpLG-1715881988741)]
[外链图片转存中…(img-omwZ7XQ1-1715881988741)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
更多推荐

所有评论(0)