registry: Add registry helper phase 1

Phase 1 of adding the registry scan helper
This commit is contained in:
Jan Schmidt 2009-03-14 23:07:40 +00:00
parent 1f4e477033
commit 51675e0c2a
18 changed files with 1901 additions and 1091 deletions

View file

@ -681,6 +681,7 @@ libs/gst/check/libcheck/Makefile
libs/gst/check/libcheck/check.h libs/gst/check/libcheck/check.h
libs/gst/controller/Makefile libs/gst/controller/Makefile
libs/gst/dataprotocol/Makefile libs/gst/dataprotocol/Makefile
libs/gst/helpers/Makefile
libs/gst/net/Makefile libs/gst/net/Makefile
plugins/Makefile plugins/Makefile
plugins/indexers/Makefile plugins/indexers/Makefile

View file

@ -33,6 +33,8 @@ gst_update_registry
<SUBSECTION Private> <SUBSECTION Private>
GST_QUARK GST_QUARK
GstQuarkId GstQuarkId
GstPluginLoader
GstPluginLoaderFuncs
</SECTION> </SECTION>
<SECTION> <SECTION>

View file

@ -82,11 +82,13 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
gstpipeline.c \ gstpipeline.c \
gstplugin.c \ gstplugin.c \
gstpluginfeature.c \ gstpluginfeature.c \
gstpluginloader.c \
gstpoll.c \ gstpoll.c \
gstpreset.c \ gstpreset.c \
gstquark.c \ gstquark.c \
gstquery.c \ gstquery.c \
gstregistry.c \ gstregistry.c \
gstregistrychunks.c \
gstsegment.c \ gstsegment.c \
gststructure.c \ gststructure.c \
gstsystemclock.c \ gstsystemclock.c \
@ -200,8 +202,10 @@ noinst_HEADERS = \
glib-compat-private.h \ glib-compat-private.h \
gst-i18n-lib.h \ gst-i18n-lib.h \
gst-i18n-app.h \ gst-i18n-app.h \
gstpluginloader.h \
gstquark.h \ gstquark.h \
gstregistrybinary.h \ gstregistrybinary.h \
gstregistrychunks.h \
gst_private.h gst_private.h
gstmarshal.h: gstmarshal.list gstmarshal.h: gstmarshal.list

View file

@ -99,13 +99,13 @@ void _gst_value_initialize (void);
/* Private registry functions */ /* Private registry functions */
gboolean _priv_gst_registry_remove_cache_plugins (GstRegistry *registry); gboolean _priv_gst_registry_remove_cache_plugins (GstRegistry *registry);
void _priv_gst_registry_cleanup (void); void _priv_gst_registry_cleanup (void);
gboolean _gst_plugin_loader_client_run (void);
/* used in both gststructure.c and gstcaps.c; numbers are completely made up */ /* used in both gststructure.c and gstcaps.c; numbers are completely made up */
#define STRUCTURE_ESTIMATED_STRING_LEN(s) (16 + (s)->fields->len * 22) #define STRUCTURE_ESTIMATED_STRING_LEN(s) (16 + (s)->fields->len * 22)
gboolean priv_gst_structure_append_to_gstring (const GstStructure * structure, gboolean priv_gst_structure_append_to_gstring (const GstStructure * structure,
GString * s); GString * s);
/* registry cache backends */ /* registry cache backends */
/* FIXME 0.11: use priv_ prefix */ /* FIXME 0.11: use priv_ prefix */
gboolean gst_registry_binary_read_cache (GstRegistry * registry, const char *location); gboolean gst_registry_binary_read_cache (GstRegistry * registry, const char *location);
@ -117,6 +117,11 @@ gboolean gst_registry_binary_write_cache (GstRegistry * registry, const char *
((c) == '-') || ((c) == '+') || ((c) == '/') || ((c) == ':') || \ ((c) == '-') || ((c) == '+') || ((c) == '/') || ((c) == ':') || \
((c) == '.')) ((c) == '.'))
#ifndef GST_DISABLE_REGISTRY
/* Secret variable to initialise gst without registry cache */
extern gboolean _gst_disable_registry_cache;
#endif
/*** debugging categories *****************************************************/ /*** debugging categories *****************************************************/
#ifndef GST_REMOVE_GST_DEBUG #ifndef GST_REMOVE_GST_DEBUG

536
gst/gstpluginloader.c Normal file
View file

@ -0,0 +1,536 @@
/* GStreamer
* Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
*
* gstpluginloader.c: GstPluginLoader helper for loading plugin files
* out of process.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifndef G_OS_WIN32
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#include <errno.h>
#include <gst/gst_private.h>
#include <gst/gstconfig.h>
#include <gst/gstpoll.h>
#include <gst/gstutils.h>
#include <gst/gstpluginloader.h>
#include <gst/gstregistrychunks.h>
#define GST_CAT_DEFAULT GST_CAT_PLUGIN_LOADING
static GstPluginLoader *plugin_loader_new (GstRegistry * registry);
static gboolean plugin_loader_free (GstPluginLoader * loader);
static gboolean plugin_loader_load (GstPluginLoader * loader,
const gchar * filename);
const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs = {
plugin_loader_new, plugin_loader_free, plugin_loader_load
};
struct _GstPluginLoader
{
GstRegistry *registry;
GstPoll *fdset;
gboolean child_started;
GPid child_pid;
GstPollFD fd_w;
GstPollFD fd_r;
gboolean is_child;
gboolean got_plugin_details;
/* Transmit buffer */
guint8 *tx_buf;
guint tx_buf_size;
guint tx_buf_write;
guint tx_buf_read;
guint32 next_tag;
guint8 *rx_buf;
guint rx_buf_size;
gboolean rx_done;
};
#define PACKET_EXIT 1
#define PACKET_LOAD_PLUGIN 2
#define PACKET_STARTING_LOAD 3
#define PACKET_PLUGIN_DETAILS 4
#define BUF_INIT_SIZE 512
#define BUF_GROW_EXTRA 512
#define HEADER_SIZE 8
#define ALIGNMENT (sizeof (void *))
static gboolean gst_plugin_loader_spawn (GstPluginLoader * loader);
static void put_packet (GstPluginLoader * loader, guint type, guint32 tag,
const guint8 * payload, guint32 payload_len);
static gboolean exchange_packets (GstPluginLoader * l);
static GstPluginLoader *
plugin_loader_new (GstRegistry * registry)
{
GstPluginLoader *l = g_new0 (GstPluginLoader, 1);
if (registry)
l->registry = gst_object_ref (registry);
l->fdset = gst_poll_new (FALSE);
gst_poll_fd_init (&l->fd_w);
gst_poll_fd_init (&l->fd_r);
l->tx_buf_size = BUF_INIT_SIZE;
l->tx_buf = g_malloc (BUF_INIT_SIZE);
l->next_tag = 0;
l->rx_buf_size = BUF_INIT_SIZE;
l->rx_buf = g_malloc (BUF_INIT_SIZE);
return l;
}
static gboolean
plugin_loader_free (GstPluginLoader * loader)
{
gboolean got_plugin_details;
fsync (loader->fd_w.fd);
if (loader->child_started) {
put_packet (loader, PACKET_EXIT, 0, NULL, 0);
/* Swap packets with the child until it exits */
while (!loader->rx_done && exchange_packets (loader)) {
};
close (loader->fd_w.fd);
close (loader->fd_r.fd);
#ifndef G_OS_WIN32
GST_LOG ("waiting for child process to exit");
waitpid (loader->child_pid, NULL, 0);
#else
g_warning ("FIXME: Implement child process shutdown for Win32");
#endif
g_spawn_close_pid (loader->child_pid);
} else {
close (loader->fd_w.fd);
close (loader->fd_r.fd);
}
gst_poll_free (loader->fdset);
g_free (loader->rx_buf);
g_free (loader->tx_buf);
if (loader->registry)
gst_object_unref (loader->registry);
got_plugin_details = loader->got_plugin_details;
g_free (loader);
return got_plugin_details;
}
static gboolean
plugin_loader_load (GstPluginLoader * loader, const gchar * filename)
{
if (!loader->child_started) {
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);
put_packet (loader, PACKET_LOAD_PLUGIN, loader->next_tag,
(guint8 *) filename, strlen (filename) + 1);
loader->next_tag++;
if (!exchange_packets (loader))
return FALSE;
return TRUE;
}
static gboolean
gst_plugin_loader_spawn (GstPluginLoader * loader)
{
char *helper_bin =
"/home/jan/devel/gstreamer/head/gstreamer/libs/gst/helpers/plugin-scanner";
char *argv[] = { helper_bin, "-l", NULL };
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->child_started = TRUE;
return TRUE;
}
gboolean
_gst_plugin_loader_client_run ()
{
GstPluginLoader *l;
l = plugin_loader_new (NULL);
if (l == NULL)
return FALSE;
l->fd_w.fd = 1; /* STDOUT */
gst_poll_add_fd (l->fdset, &l->fd_w);
l->fd_r.fd = 0; /* STDIN */
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));
plugin_loader_free (l);
return TRUE;
}
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) {
l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
}
out = l->tx_buf + l->tx_buf_write;
out[0] = type;
GST_WRITE_UINT24_BE (out + 1, tag);
GST_WRITE_UINT32_BE (out + 4, payload_len);
memcpy (out + HEADER_SIZE, payload, payload_len);
l->tx_buf_write += len;
gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
}
static void
put_chunk (GstPluginLoader * l, GstRegistryChunk * chunk, guint * pos)
{
guint padsize = 0;
guint len;
guint8 *out;
/* Might need to align the chunk */
if (chunk->align && ((*pos) % ALIGNMENT) != 0)
padsize = ALIGNMENT - ((*pos) % ALIGNMENT);
len = padsize + chunk->size;
if (l->tx_buf_write + len >= l->tx_buf_size) {
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;
memcpy (out + padsize, chunk->data, chunk->size);
l->tx_buf_write += len;
*pos += len;
gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
};
static gboolean
write_one (GstPluginLoader * l)
{
guint8 *out;
guint32 to_write;
int res;
if (l->tx_buf_read + HEADER_SIZE > l->tx_buf_write)
return FALSE;
out = l->tx_buf + l->tx_buf_read;
to_write = GST_READ_UINT32_BE (out + 4) + HEADER_SIZE;
l->tx_buf_read += to_write;
GST_LOG ("Writing packet of size %d bytes to fd %d", to_write, l->fd_w.fd);
do {
res = write (l->fd_w.fd, out, to_write);
if (res > 0) {
to_write -= res;
out += res;
}
} while (to_write > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
if (l->tx_buf_read == l->tx_buf_write) {
gst_poll_fd_ctl_write (l->fdset, &l->fd_w, FALSE);
l->tx_buf_read = l->tx_buf_write = 0;
}
return TRUE;
}
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\n", filename, tag);
put_packet (l, PACKET_STARTING_LOAD, tag, NULL, 0);
newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
if (newplugin) {
guint hdr_pos;
guint offset;
/* Now serialise the plugin details and send */
if (!_priv_gst_registry_chunks_save_plugin (&chunks,
gst_registry_get_default (), 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;
for (walk = chunks; walk; walk = g_list_next (walk)) {
GstRegistryChunk *cur = walk->data;
put_chunk (l, cur, &offset);
if (!(cur->flags & GST_REGISTRY_CHUNK_FLAG_CONST))
g_free (cur->data);
g_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;
if (!(cur->flags & GST_REGISTRY_CHUNK_FLAG_CONST))
g_free (cur->data);
g_free (cur);
}
g_list_free (chunks);
}
return FALSE;
}
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, then we keep looping until the parent closes the fd */
put_packet (l, PACKET_EXIT, 0, NULL, 0);
} else {
l->rx_done = TRUE; /* All done reading from child */
}
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_STARTING_LOAD:
GST_LOG_OBJECT (l->registry,
"child started loading plugin w/ tag %u", tag);
break;
case PACKET_PLUGIN_DETAILS:{
gchar *tmp = (gchar *) payload;
GST_DEBUG_OBJECT (l->registry,
"child loaded plugin w/ tag %u. %d bytes info", tag, payload_len);
if (payload_len > 0) {
GstPlugin *newplugin;
_priv_gst_registry_chunks_load_plugin (l->registry, &tmp,
tmp + payload_len, &newplugin);
newplugin->flags &= ~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;
}
break;
}
default:
return FALSE; /* Invalid packet -> something is wrong */
}
return res;
}
static gboolean
read_one (GstPluginLoader * l)
{
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 (res > 0) {
to_read -= res;
in += res;
}
} while (to_read > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
if (res < 0) {
GST_LOG ("Failed reading packet header");
return FALSE;
}
packet_len = GST_READ_UINT32_BE (l->rx_buf + 4);
if (packet_len + HEADER_SIZE >= l->rx_buf_size) {
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 (res > 0) {
to_read -= res;
in += res;
}
} while (to_read > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
if (res < 0) {
GST_ERROR ("Packet payload read failed");
return FALSE;
}
tag = GST_READ_UINT24_BE (l->rx_buf + 1);
return handle_rx_packet (l, l->rx_buf[0], tag,
l->rx_buf + HEADER_SIZE, packet_len);
}
static gboolean
exchange_packets (GstPluginLoader * l)
{
gint res;
/* Wait for activity on our FDs */
do {
do {
res = gst_poll_wait (l->fdset, GST_CLOCK_TIME_NONE);
} while (res == -1 && (errno == EINTR || errno == EAGAIN));
if (res < 0)
return FALSE;
GST_DEBUG ("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_poll_fd_has_closed (l->fdset, &l->fd_r)) {
GST_LOG ("read fd %d closed/errored", l->fd_r.fd);
return FALSE;
}
if (gst_poll_fd_can_read (l->fdset, &l->fd_r)) {
if (!read_one (l))
return FALSE;
}
}
if (l->tx_buf_read < l->tx_buf_write) {
if (gst_poll_fd_has_error (l->fdset, &l->fd_w) ||
gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
GST_ERROR ("write fd %d closed/errored", l->fd_w.fd);
return FALSE;
}
if (gst_poll_fd_can_write (l->fdset, &l->fd_w)) {
if (!write_one (l))
return FALSE;
}
}
} while (l->tx_buf_read < l->tx_buf_write);
return TRUE;
}

38
gst/gstpluginloader.h Normal file
View file

@ -0,0 +1,38 @@
/* GStreamer
* Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
*
* gstpluginloader.h: Helper for out-of-process plugin loading. Private header.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_PLUGINLOADER_H__
#define __GST_PLUGINLOADER_H__
G_BEGIN_DECLS
typedef struct _GstPluginLoader GstPluginLoader;
typedef struct _GstPluginLoaderFuncs {
GstPluginLoader * (*create)(GstRegistry *registry);
gboolean (*destroy)(GstPluginLoader *loader);
gboolean (*load)(GstPluginLoader *loader, const gchar *filename);
} GstPluginLoaderFuncs;
extern const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs;
G_END_DECLS
#endif /* __GST_PLUGINLOADER_H__ */

View file

@ -113,6 +113,8 @@
#include "gstmarshal.h" #include "gstmarshal.h"
#include "gstfilter.h" #include "gstfilter.h"
#include "gstpluginloader.h"
#include "gst-i18n-lib.h" #include "gst-i18n-lib.h"
#define GST_CAT_DEFAULT GST_CAT_REGISTRY #define GST_CAT_DEFAULT GST_CAT_REGISTRY
@ -138,6 +140,9 @@ extern GSList *_priv_gst_preload_plugins;
/*set to TRUE when registry needn't to be updated */ /*set to TRUE when registry needn't to be updated */
gboolean _priv_gst_disable_registry_update = FALSE; gboolean _priv_gst_disable_registry_update = FALSE;
extern GList *_priv_gst_plugin_paths; extern GList *_priv_gst_plugin_paths;
/* Set to TRUE when the registry cache should be disabled */
gboolean _gst_disable_registry_cache = FALSE;
#endif #endif
/* Element signals and args */ /* Element signals and args */
@ -819,15 +824,109 @@ gst_registry_lookup (GstRegistry * registry, const char *filename)
return plugin; return plugin;
} }
typedef enum
{
REGISTRY_SCAN_HELPER_NOT_STARTED = 0,
REGISTRY_SCAN_HELPER_DISABLED,
REGISTRY_SCAN_HELPER_RUNNING
} GstRegistryScanHelperState;
typedef struct
{
GstRegistry *registry;
GstRegistryScanHelperState helper_state;
GstPluginLoader *helper;
gboolean changed;
} GstRegistryScanContext;
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;
}
static void
clear_scan_context (GstRegistryScanContext * context)
{
if (context->helper) {
context->changed |= _priv_gst_plugin_loader_funcs.destroy (context->helper);
context->helper = NULL;
}
}
static gboolean static gboolean
gst_registry_scan_path_level (GstRegistry * registry, const gchar * path, gst_registry_scan_plugin_file (GstRegistryScanContext * context,
int level) const gchar * filename)
{
gboolean changed = FALSE;
GstPlugin *newplugin = NULL;
/* 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
context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
}
if (context->helper_state == REGISTRY_SCAN_HELPER_RUNNING) {
GST_DEBUG ("Using scan-helper to load plugin %s", filename);
if (!_priv_gst_plugin_loader_funcs.load (context->helper, filename)) {
g_warning ("External plugin loader failed...");
context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
}
} else {
/* 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)
*/
newplugin = gst_plugin_load_file (filename, NULL);
}
if (newplugin) {
GST_DEBUG_OBJECT (context->registry, "marking new plugin %p as registered",
newplugin);
newplugin->registered = TRUE;
gst_object_unref (newplugin);
changed = TRUE;
}
return changed;
}
static gboolean
gst_registry_scan_path_level (GstRegistryScanContext * context,
const gchar * path, int level)
{ {
GDir *dir; GDir *dir;
const gchar *dirent; const gchar *dirent;
gchar *filename; gchar *filename;
GstPlugin *plugin; GstPlugin *plugin;
GstPlugin *newplugin;
gboolean changed = FALSE; gboolean changed = FALSE;
dir = g_dir_open (path, 0, NULL); dir = g_dir_open (path, 0, NULL);
@ -849,7 +948,7 @@ gst_registry_scan_path_level (GstRegistry * registry, const gchar * path,
/* skip the .debug directory, these contain elf files that are not /* skip the .debug directory, these contain elf files that are not
* useful or worse, can crash dlopen () */ * useful or worse, can crash dlopen () */
if (g_str_equal (dirent, ".debug") || g_str_equal (dirent, ".git")) { if (g_str_equal (dirent, ".debug") || g_str_equal (dirent, ".git")) {
GST_LOG_OBJECT (registry, "ignoring .debug or .git directory"); GST_LOG_OBJECT (context->registry, "ignoring .debug or .git directory");
g_free (filename); g_free (filename);
continue; continue;
} }
@ -857,17 +956,19 @@ gst_registry_scan_path_level (GstRegistry * registry, const gchar * path,
* is inconsistent with other PATH environment variables * is inconsistent with other PATH environment variables
*/ */
if (level > 0) { if (level > 0) {
GST_LOG_OBJECT (registry, "recursing into directory %s", filename); GST_LOG_OBJECT (context->registry, "recursing into directory %s",
changed |= gst_registry_scan_path_level (registry, filename, level - 1); filename);
changed |= gst_registry_scan_path_level (context, filename, level - 1);
} else { } else {
GST_LOG_OBJECT (registry, "not recursing into directory %s, " GST_LOG_OBJECT (context->registry, "not recursing into directory %s, "
"recursion level too deep", filename); "recursion level too deep", filename);
} }
g_free (filename); g_free (filename);
continue; continue;
} }
if (!(file_status.st_mode & S_IFREG)) { if (!(file_status.st_mode & S_IFREG)) {
GST_LOG_OBJECT (registry, "%s is not a regular file, ignoring", filename); GST_LOG_OBJECT (context->registry, "%s is not a regular file, ignoring",
filename);
g_free (filename); g_free (filename);
continue; continue;
} }
@ -876,22 +977,24 @@ gst_registry_scan_path_level (GstRegistry * registry, const gchar * path,
&& !g_str_has_suffix (dirent, GST_EXTRA_MODULE_SUFFIX) && !g_str_has_suffix (dirent, GST_EXTRA_MODULE_SUFFIX)
#endif #endif
) { ) {
GST_LOG_OBJECT (registry, "extension is not recognized as module file, " GST_LOG_OBJECT (context->registry,
"ignoring file %s", filename); "extension is not recognized as module file, ignoring file %s",
filename);
g_free (filename); g_free (filename);
continue; continue;
} }
GST_LOG_OBJECT (registry, "file %s looks like a possible module", filename); GST_LOG_OBJECT (context->registry, "file %s looks like a possible module",
filename);
/* plug-ins are considered unique by basename; if the given name /* plug-ins are considered unique by basename; if the given name
* was already seen by the registry, we ignore it */ * was already seen by the registry, we ignore it */
plugin = gst_registry_lookup (registry, filename); plugin = gst_registry_lookup (context->registry, filename);
if (plugin) { if (plugin) {
gboolean env_vars_changed, deps_changed = FALSE; gboolean env_vars_changed, deps_changed = FALSE;
if (plugin->registered) { if (plugin->registered) {
GST_DEBUG_OBJECT (registry, GST_DEBUG_OBJECT (context->registry,
"plugin already registered from path \"%s\"", "plugin already registered from path \"%s\"",
GST_STR_NULL (plugin->filename)); GST_STR_NULL (plugin->filename));
g_free (filename); g_free (filename);
@ -901,52 +1004,37 @@ gst_registry_scan_path_level (GstRegistry * registry, const gchar * path,
env_vars_changed = _priv_plugin_deps_env_vars_changed (plugin); 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 && if (plugin->file_mtime == file_status.st_mtime &&
plugin->file_size == file_status.st_size && !env_vars_changed && plugin->file_size == file_status.st_size && !env_vars_changed &&
!(deps_changed = _priv_plugin_deps_files_changed (plugin))) { !(deps_changed = _priv_plugin_deps_files_changed (plugin)) &&
GST_LOG_OBJECT (registry, "file %s cached", filename); strcmp (plugin->filename, filename) == 0) {
GST_LOG_OBJECT (context->registry, "file %s cached", filename);
plugin->flags &= ~GST_PLUGIN_FLAG_CACHED; plugin->flags &= ~GST_PLUGIN_FLAG_CACHED;
GST_LOG_OBJECT (registry, "marking plugin %p as registered as %s", GST_LOG_OBJECT (context->registry,
plugin, filename); "marking plugin %p as registered as %s", plugin, filename);
plugin->registered = TRUE; plugin->registered = TRUE;
/* Update the file path on which we've seen this cached plugin
* to ensure the registry cache will reflect up to date information */
if (strcmp (plugin->filename, filename) != 0) {
g_free (plugin->filename);
plugin->filename = g_strdup (filename);
changed = TRUE;
}
} else { } else {
GST_INFO_OBJECT (registry, "cached info for %s is stale", filename); GST_INFO_OBJECT (context->registry, "cached info for %s is stale",
GST_DEBUG_OBJECT (registry, "mtime %ld != %ld or size %" filename);
GST_DEBUG_OBJECT (context->registry, "mtime %ld != %ld or size %"
G_GINT64_FORMAT " != %" G_GINT64_FORMAT " or external dependency " G_GINT64_FORMAT " != %" G_GINT64_FORMAT " or external dependency "
"env_vars changed: %d or external dependencies changed: %d", "env_vars changed: %d or external dependencies changed: %d",
plugin->file_mtime, file_status.st_mtime, plugin->file_mtime, file_status.st_mtime,
(gint64) plugin->file_size, (gint64) file_status.st_size, (gint64) plugin->file_size, (gint64) file_status.st_size,
env_vars_changed, deps_changed); env_vars_changed, deps_changed);
gst_registry_remove_plugin (gst_registry_get_default (), plugin); gst_registry_remove_plugin (context->registry, plugin);
/* We don't use a GError here because a failure to load some shared changed |= gst_registry_scan_plugin_file (context, filename);
* objects as plugins is normal (particularly in the uninstalled case)
*/
newplugin = gst_plugin_load_file (filename, NULL);
if (newplugin) {
GST_DEBUG_OBJECT (registry, "marking new plugin %p as registered",
newplugin);
newplugin->registered = TRUE;
gst_object_unref (newplugin);
}
changed = TRUE;
} }
gst_object_unref (plugin); gst_object_unref (plugin);
} else { } else {
GST_DEBUG_OBJECT (registry, "file %s not yet in registry", filename); GST_DEBUG_OBJECT (context->registry, "file %s not yet in registry",
newplugin = gst_plugin_load_file (filename, NULL); filename);
if (newplugin) { changed |= gst_registry_scan_plugin_file (context, filename);
newplugin->registered = TRUE;
gst_object_unref (newplugin);
changed = TRUE;
}
} }
g_free (filename); g_free (filename);
@ -957,6 +1045,20 @@ gst_registry_scan_path_level (GstRegistry * registry, const gchar * path,
return changed; return changed;
} }
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: * gst_registry_scan_path:
* @registry: the registry to add found plugins to * @registry: the registry to add found plugins to
@ -970,17 +1072,20 @@ gst_registry_scan_path_level (GstRegistry * registry, const gchar * path,
gboolean gboolean
gst_registry_scan_path (GstRegistry * registry, const gchar * path) gst_registry_scan_path (GstRegistry * registry, const gchar * path)
{ {
gboolean changed; GstRegistryScanContext context;
gboolean result;
g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE); g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
g_return_val_if_fail (path != NULL, FALSE); g_return_val_if_fail (path != NULL, FALSE);
GST_DEBUG_OBJECT (registry, "scanning path %s", path); init_scan_context (&context, registry);
changed = gst_registry_scan_path_level (registry, path, 10);
GST_DEBUG_OBJECT (registry, "registry changed in path %s: %d", path, changed); result = gst_registry_scan_path_internal (&context, path);
return changed; clear_scan_context (&context);
result |= context.changed;
return result;
} }
static gboolean static gboolean
@ -1157,8 +1262,12 @@ scan_and_update_registry (GstRegistry * default_registry,
const gchar *plugin_path; const gchar *plugin_path;
gboolean changed = FALSE; gboolean changed = FALSE;
GList *l; GList *l;
GstRegistryScanContext context;
GST_INFO ("Validating plugins from registry cache: %s", registry_file);
init_scan_context (&context, default_registry);
GST_INFO ("Validating registry cache: %s", registry_file);
/* It sounds tempting to just compare the mtime of directories with the mtime /* 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 * of the registry cache, but it does not work. It would not catch updated
* plugins, which might bring more or less features. * plugins, which might bring more or less features.
@ -1168,7 +1277,7 @@ scan_and_update_registry (GstRegistry * default_registry,
GST_DEBUG ("scanning paths added via --gst-plugin-path"); GST_DEBUG ("scanning paths added via --gst-plugin-path");
for (l = _priv_gst_plugin_paths; l != NULL; l = l->next) { for (l = _priv_gst_plugin_paths; l != NULL; l = l->next) {
GST_INFO ("Scanning plugin path: \"%s\"", (gchar *) l->data); GST_INFO ("Scanning plugin path: \"%s\"", (gchar *) l->data);
changed |= gst_registry_scan_path (default_registry, (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 */ /* keep plugin_paths around in case a re-scan is forced later on */
@ -1182,7 +1291,7 @@ scan_and_update_registry (GstRegistry * default_registry,
GST_DEBUG ("GST_PLUGIN_PATH set to %s", plugin_path); GST_DEBUG ("GST_PLUGIN_PATH set to %s", plugin_path);
list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0); list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; list[i]; i++) { for (i = 0; list[i]; i++) {
changed |= gst_registry_scan_path (default_registry, list[i]); changed |= gst_registry_scan_path_internal (&context, list[i]);
} }
g_strfreev (list); g_strfreev (list);
} else { } else {
@ -1203,12 +1312,12 @@ scan_and_update_registry (GstRegistry * default_registry,
home_plugins = g_build_filename (g_get_home_dir (), home_plugins = g_build_filename (g_get_home_dir (),
".gstreamer-" GST_MAJORMINOR, "plugins", NULL); ".gstreamer-" GST_MAJORMINOR, "plugins", NULL);
GST_DEBUG ("scanning home plugins %s", home_plugins); GST_DEBUG ("scanning home plugins %s", home_plugins);
changed |= gst_registry_scan_path (default_registry, home_plugins); changed |= gst_registry_scan_path_internal (&context, home_plugins);
g_free (home_plugins); g_free (home_plugins);
/* add the main (installed) library path */ /* add the main (installed) library path */
GST_DEBUG ("scanning main plugins %s", PLUGINDIR); GST_DEBUG ("scanning main plugins %s", PLUGINDIR);
changed |= gst_registry_scan_path (default_registry, PLUGINDIR); changed |= gst_registry_scan_path_internal (&context, PLUGINDIR);
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
{ {
@ -1222,7 +1331,7 @@ scan_and_update_registry (GstRegistry * default_registry,
dir = g_build_filename (base_dir, "lib", "gstreamer-0.10", NULL); dir = g_build_filename (base_dir, "lib", "gstreamer-0.10", NULL);
GST_DEBUG ("scanning DLL dir %s", dir); GST_DEBUG ("scanning DLL dir %s", dir);
changed |= gst_registry_scan_path (default_registry, dir); changed |= gst_registry_scan_path_internal (&context, dir);
g_free (dir); g_free (dir);
g_free (base_dir); g_free (base_dir);
@ -1235,11 +1344,14 @@ scan_and_update_registry (GstRegistry * default_registry,
GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH set to %s", plugin_path); GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH set to %s", plugin_path);
list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0); list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; list[i]; i++) { for (i = 0; list[i]; i++) {
changed |= gst_registry_scan_path (default_registry, list[i]); changed |= gst_registry_scan_path_internal (&context, list[i]);
} }
g_strfreev (list); g_strfreev (list);
} }
clear_scan_context (&context);
changed |= context.changed;
/* Remove cached plugins so stale info is cleared. */ /* Remove cached plugins so stale info is cleared. */
changed |= gst_registry_remove_cache_plugins (default_registry); changed |= gst_registry_remove_cache_plugins (default_registry);
@ -1265,131 +1377,14 @@ scan_and_update_registry (GstRegistry * default_registry,
return REGISTRY_SCAN_AND_UPDATE_SUCCESS_UPDATED; return REGISTRY_SCAN_AND_UPDATE_SUCCESS_UPDATED;
} }
static gboolean
ensure_current_registry_nonforking (GstRegistry * default_registry,
const gchar * registry_file, GError ** error)
{
/* fork() not available */
GST_DEBUG ("Updating registry cache in-process");
scan_and_update_registry (default_registry, registry_file, TRUE, error);
return TRUE;
}
/* when forking is not available this function always does nothing but return
* TRUE immediatly */
static gboolean
ensure_current_registry_forking (GstRegistry * default_registry,
const gchar * registry_file, GError ** error)
{
#ifdef HAVE_FORK
pid_t pid;
int pfd[2];
int ret;
/* We fork here, and let the child read and possibly rebuild the registry.
* After that, the parent will re-read the freshly generated registry. */
GST_DEBUG ("forking to update registry");
if (pipe (pfd) == -1) {
g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
_("Error re-scanning registry %s: %s"),
", could not create pipes. Error", g_strerror (errno));
return FALSE;
}
pid = fork ();
if (pid == -1) {
GST_ERROR ("Failed to fork()");
g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
_("Error re-scanning registry %s: %s"),
", failed to fork. Error", g_strerror (errno));
return FALSE;
}
if (pid == 0) {
gint result_code;
/* this is the child. Close the read pipe */
(void) close (pfd[0]);
GST_DEBUG ("child reading registry cache");
result_code =
scan_and_update_registry (default_registry, registry_file, TRUE, NULL);
/* need to use _exit, so that any exit handlers registered don't
* bring down the main program */
GST_DEBUG ("child exiting: %d", result_code);
/* make valgrind happy (yes, you can call it insane) */
g_free ((char *) registry_file);
/* write a result byte to the pipe */
do {
ret = write (pfd[1], &result_code, sizeof (result_code));
} while (ret == -1 && errno == EINTR);
/* if ret == -1 now, we could not write to pipe, probably
* means parent has exited before us */
(void) close (pfd[1]);
_exit (0);
} else {
gint result_code;
/* parent. Close write pipe */
(void) close (pfd[1]);
/* Wait for result from the pipe */
GST_DEBUG ("Waiting for data from child");
do {
ret = read (pfd[0], &result_code, sizeof (result_code));
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
_("Error re-scanning registry %s: %s"),
", read returned error", g_strerror (errno));
close (pfd[0]);
return FALSE;
}
(void) close (pfd[0]);
/* Wait to ensure the child is reaped, but ignore the result */
GST_DEBUG ("parent waiting on child");
waitpid (pid, NULL, 0);
GST_DEBUG ("parent done waiting on child");
if (ret == 0) {
GST_ERROR ("child did not exit normally, terminated by signal");
g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
_("Error re-scanning registry %s"), ", child terminated by signal");
return FALSE;
}
if (result_code == REGISTRY_SCAN_AND_UPDATE_SUCCESS_UPDATED) {
GST_DEBUG ("Child succeeded. Parent reading registry cache");
gst_registry_remove_cache_plugins (default_registry);
#ifdef USE_BINARY_REGISTRY
gst_registry_binary_read_cache (default_registry, registry_file);
#else
gst_registry_xml_read_cache (default_registry, registry_file);
#endif
} else if (result_code == REGISTRY_SCAN_AND_UPDATE_FAILURE) {
GST_DEBUG ("Child failed. Parent re-scanning registry, ignoring errors.");
scan_and_update_registry (default_registry, registry_file, FALSE, NULL);
}
}
#endif /* HAVE_FORK */
return TRUE;
}
static gboolean static gboolean
ensure_current_registry (GError ** error) ensure_current_registry (GError ** error)
{ {
gchar *registry_file; gchar *registry_file;
GstRegistry *default_registry; GstRegistry *default_registry;
gboolean ret = TRUE; gboolean ret = TRUE;
gboolean do_update; gboolean do_update = TRUE;
gboolean have_cache; gboolean have_cache = TRUE;
default_registry = gst_registry_get_default (); default_registry = gst_registry_get_default ();
registry_file = g_strdup (g_getenv ("GST_REGISTRY")); registry_file = g_strdup (g_getenv ("GST_REGISTRY"));
@ -1398,8 +1393,11 @@ ensure_current_registry (GError ** error)
".gstreamer-" GST_MAJORMINOR, "registry." HOST_CPU ".bin", NULL); ".gstreamer-" GST_MAJORMINOR, "registry." HOST_CPU ".bin", NULL);
} }
GST_INFO ("reading registry cache: %s", registry_file); if (!_gst_disable_registry_cache) {
have_cache = gst_registry_binary_read_cache (default_registry, registry_file); GST_INFO ("reading registry cache: %s", registry_file);
have_cache = gst_registry_binary_read_cache (default_registry,
registry_file);
}
if (have_cache) { if (have_cache) {
do_update = !_priv_gst_disable_registry_update; do_update = !_priv_gst_disable_registry_update;
@ -1410,8 +1408,6 @@ ensure_current_registry (GError ** error)
/* do update for any value different from "no" */ /* do update for any value different from "no" */
do_update = (strcmp (update_env, "no") != 0); do_update = (strcmp (update_env, "no") != 0);
} }
} else {
do_update = TRUE;
} }
} }

View file

@ -24,7 +24,6 @@
#ifndef __GST_REGISTRY_H__ #ifndef __GST_REGISTRY_H__
#define __GST_REGISTRY_H__ #define __GST_REGISTRY_H__
#include <stdio.h> /* FIXME: because of cache_file below */
#include <gst/gstconfig.h> #include <gst/gstconfig.h>
#include <gst/gstplugin.h> #include <gst/gstplugin.h>
#include <gst/gstpluginfeature.h> #include <gst/gstpluginfeature.h>

File diff suppressed because it is too large Load diff

View file

@ -33,6 +33,8 @@
#include <gst/gstpad.h> #include <gst/gstpad.h>
#include <gst/gstregistry.h> #include <gst/gstregistry.h>
G_BEGIN_DECLS
/* /*
* GST_MAGIC_BINARY_REGISTRY_STR: * GST_MAGIC_BINARY_REGISTRY_STR:
* *
@ -68,117 +70,11 @@ typedef struct _GstBinaryRegistryMagic
gchar version[GST_MAGIC_BINARY_VERSION_LEN]; gchar version[GST_MAGIC_BINARY_VERSION_LEN];
} GstBinaryRegistryMagic; } GstBinaryRegistryMagic;
/*
* we reference strings directly from the plugins and in this case set CONST to
* avoid freeing them
*/
enum {
GST_BINARY_REGISTRY_FLAG_NONE = 0,
GST_BINARY_REGISTRY_FLAG_CONST = 1
};
/*
* GstBinaryChunk:
*
* Header for binary blobs
*/
typedef struct _GstBinaryChunk
{
gpointer data;
guint size;
guint flags;
gboolean align;
} GstBinaryChunk;
/*
* GstBinaryPluginElement:
*
* @nfeatures: says how many binary plugin feature structures we will have
* right after the structure itself.
*
* A structure containing (staticely) every information needed for a plugin
*/
typedef struct _GstBinaryPluginElement
{
gulong file_size;
gulong file_mtime;
guint n_deps;
guint nfeatures;
} GstBinaryPluginElement;
/* GstBinaryDep:
*/
typedef struct _GstBinaryDep
{
guint flags;
guint n_env_vars;
guint n_paths;
guint n_names;
guint env_hash;
guint stat_hash;
} GstBinaryDep;
/*
* GstBinaryPluginFeature:
* @rank: rank of the feature
*
* A structure containing the plugin features
*/
typedef struct _GstBinaryPluginFeature
{
gulong rank;
} GstBinaryPluginFeature;
/*
* GstBinaryElementFactory:
* @npadtemplates: stores the number of GstBinaryPadTemplate structures
* following the structure
* @ninterfaces: stores the number of interface names following the structure
* @nuriprotocols: stores the number of protocol strings following the structure
*
* A structure containing the element factory fields
*/
typedef struct _GstBinaryElementFactory
{
GstBinaryPluginFeature plugin_feature;
guint npadtemplates;
guint ninterfaces;
guint nuriprotocols;
} GstBinaryElementFactory;
/*
* GstBinaryTypeFindFactory:
* @nextensions: stores the number of typefind extensions
*
* A structure containing the element factory fields
*/
typedef struct _GstBinaryTypeFindFactory
{
GstBinaryPluginFeature plugin_feature;
guint nextensions;
} GstBinaryTypeFindFactory;
/*
* GstBinaryPadTemplate:
*
* A structure containing the static pad templates of a plugin feature
*/
typedef struct _GstBinaryPadTemplate
{
guint direction; /* Either 0:"sink" or 1:"src" */
GstPadPresence presence;
} GstBinaryPadTemplate;
/* Function prototypes */ /* Function prototypes */
gboolean gst_registry_binary_write_cache(GstRegistry *registry, const char *location); gboolean gst_registry_binary_write_cache(GstRegistry *registry, const char *location);
gboolean gst_registry_binary_read_cache(GstRegistry *registry, const char *location); gboolean gst_registry_binary_read_cache(GstRegistry *registry, const char *location);
G_END_DECLS
#endif /* !__GST_REGISTRYBINARY_H__ */ #endif /* !__GST_REGISTRYBINARY_H__ */

798
gst/gstregistrychunks.c Normal file
View file

@ -0,0 +1,798 @@
/* GStreamer
* Copyright (C) 2006 Josep Torra <josep@fluendo.com>
* 2006 Mathieu Garcia <matthieu@fluendo.com>
* 2006,2007 Stefan Kost <ensonic@users.sf.net>
* 2008 Sebastian Dröge <slomo@circular-chaos.org>
* 2008 Jan Schmidt <jan.schmidt@sun.com>
*
* gstregistrychunks.c: GstRegistryChunk helper for serialising/deserialising
* plugin entries and features.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gst/gst_private.h>
#include <gst/gstconfig.h>
#include <gst/gstelement.h>
#include <gst/gsttypefind.h>
#include <gst/gsttypefindfactory.h>
#include <gst/gsturi.h>
#include <gst/gstinfo.h>
#include <gst/gstenumtypes.h>
#include <gst/gstpadtemplate.h>
#include <gst/gstregistrychunks.h>
#define GST_CAT_DEFAULT GST_CAT_REGISTRY
/* count string length, but return -1 if we hit the eof */
static gint
_strnlen (const gchar * str, gint maxlen)
{
gint len = 0;
if (G_UNLIKELY (len == maxlen))
return -1;
while (*str++ != '\0') {
len++;
if (G_UNLIKELY (len == maxlen))
return -1;
}
return len;
}
/* Macros */
#define unpack_element(inptr, outptr, element, endptr, error_label) G_STMT_START{ \
if (inptr + sizeof(element) >= endptr) \
goto error_label; \
outptr = (element *) inptr; \
inptr += sizeof (element); \
}G_STMT_END
#define unpack_const_string(inptr, outptr, endptr, error_label) G_STMT_START{\
gint _len = _strnlen (inptr, (endptr-inptr)); \
if (_len == -1) \
goto error_label; \
outptr = g_intern_string ((const gchar *)inptr); \
inptr += _len + 1; \
}G_STMT_END
#define unpack_string(inptr, outptr, endptr, error_label) G_STMT_START{\
gint _len = _strnlen (inptr, (endptr-inptr)); \
if (_len == -1) \
goto error_label; \
outptr = g_memdup ((gconstpointer)inptr, _len + 1); \
inptr += _len + 1; \
}G_STMT_END
#define ALIGNMENT (sizeof (void *))
#define alignment(_address) (gsize)_address%ALIGNMENT
#define align(_ptr) _ptr += (( alignment(_ptr) == 0) ? 0 : ALIGNMENT-alignment(_ptr))
/*
* gst_registry_chunks_save_const_string:
*
* Store a const string in a binary chunk.
*
* Returns: %TRUE for success
*/
inline static gboolean
gst_registry_chunks_save_const_string (GList ** list, const gchar * str)
{
GstRegistryChunk *chunk;
if (G_UNLIKELY (str == NULL)) {
GST_ERROR ("unexpected NULL string in plugin or plugin feature data");
str = "";
}
chunk = g_malloc (sizeof (GstRegistryChunk));
chunk->data = (gpointer) str;
chunk->size = strlen ((gchar *) chunk->data) + 1;
chunk->flags = GST_REGISTRY_CHUNK_FLAG_CONST;
chunk->align = FALSE;
*list = g_list_prepend (*list, chunk);
return TRUE;
}
/*
* gst_registry_chunks_save_string:
*
* Store a string in a binary chunk.
*
* Returns: %TRUE for success
*/
inline static gboolean
gst_registry_chunks_save_string (GList ** list, gchar * str)
{
GstRegistryChunk *chunk;
chunk = g_malloc (sizeof (GstRegistryChunk));
chunk->data = str;
chunk->size = strlen ((gchar *) chunk->data) + 1;
chunk->flags = GST_REGISTRY_CHUNK_FLAG_NONE;
chunk->align = FALSE;
*list = g_list_prepend (*list, chunk);
return TRUE;
}
/*
* gst_registry_chunks_save_data:
*
* Store some data in a binary chunk.
*
* Returns: the initialized chunk
*/
inline static GstRegistryChunk *
gst_registry_chunks_make_data (gpointer data, gulong size)
{
GstRegistryChunk *chunk;
chunk = g_malloc (sizeof (GstRegistryChunk));
chunk->data = data;
chunk->size = size;
chunk->flags = GST_REGISTRY_CHUNK_FLAG_NONE;
chunk->align = TRUE;
return chunk;
}
/*
* gst_registry_chunks_save_pad_template:
*
* Store pad_templates in binary chunks.
*
* Returns: %TRUE for success
*/
static gboolean
gst_registry_chunks_save_pad_template (GList ** list,
GstStaticPadTemplate * template)
{
GstRegistryChunkPadTemplate *pt;
GstRegistryChunk *chk;
pt = g_malloc0 (sizeof (GstRegistryChunkPadTemplate));
chk =
gst_registry_chunks_make_data (pt, sizeof (GstRegistryChunkPadTemplate));
pt->presence = template->presence;
pt->direction = template->direction;
/* pack pad template strings */
gst_registry_chunks_save_const_string (list,
(gchar *) (template->static_caps.string));
gst_registry_chunks_save_const_string (list, template->name_template);
*list = g_list_prepend (*list, chk);
return TRUE;
}
/*
* gst_registry_chunks_save_feature:
*
* Store features in binary chunks.
*
* Returns: %TRUE for success
*/
static gboolean
gst_registry_chunks_save_feature (GList ** list, GstPluginFeature * feature)
{
const gchar *type_name = g_type_name (G_OBJECT_TYPE (feature));
GstRegistryChunkPluginFeature *pf = NULL;
GstRegistryChunk *chk = NULL;
GList *walk;
if (!type_name) {
GST_ERROR ("NULL feature type_name, aborting.");
return FALSE;
}
if (GST_IS_ELEMENT_FACTORY (feature)) {
GstRegistryChunkElementFactory *ef;
GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
ef = g_malloc0 (sizeof (GstRegistryChunkElementFactory));
chk =
gst_registry_chunks_make_data (ef,
sizeof (GstRegistryChunkElementFactory));
ef->npadtemplates = ef->ninterfaces = ef->nuriprotocols = 0;
pf = (GstRegistryChunkPluginFeature *) ef;
/* save interfaces */
for (walk = factory->interfaces; walk;
walk = g_list_next (walk), ef->ninterfaces++) {
gst_registry_chunks_save_const_string (list, (gchar *) walk->data);
}
GST_DEBUG ("Saved %d Interfaces", ef->ninterfaces);
/* save uritypes */
if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
if (factory->uri_protocols && *factory->uri_protocols) {
GstRegistryChunk *subchk;
gchar **protocol;
subchk =
gst_registry_chunks_make_data (&factory->uri_type,
sizeof (factory->uri_type));
subchk->flags = GST_REGISTRY_CHUNK_FLAG_CONST;
protocol = factory->uri_protocols;
while (*protocol) {
gst_registry_chunks_save_const_string (list, *protocol++);
ef->nuriprotocols++;
}
*list = g_list_prepend (*list, subchk);
GST_DEBUG ("Saved %d UriTypes", ef->nuriprotocols);
} else {
g_warning ("GStreamer feature '%s' is URI handler but does not provide"
" any protocols it can handle", feature->name);
}
}
/* save pad-templates */
for (walk = factory->staticpadtemplates; walk;
walk = g_list_next (walk), ef->npadtemplates++) {
GstStaticPadTemplate *template = walk->data;
if (!gst_registry_chunks_save_pad_template (list, template)) {
GST_ERROR ("Can't fill pad template, aborting.");
goto fail;
}
}
/* pack element factory strings */
gst_registry_chunks_save_const_string (list, factory->details.author);
gst_registry_chunks_save_const_string (list, factory->details.description);
gst_registry_chunks_save_const_string (list, factory->details.klass);
gst_registry_chunks_save_const_string (list, factory->details.longname);
} else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
GstRegistryChunkTypeFindFactory *tff;
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
gchar *str;
tff = g_malloc0 (sizeof (GstRegistryChunkTypeFindFactory));
chk =
gst_registry_chunks_make_data (tff,
sizeof (GstRegistryChunkTypeFindFactory));
tff->nextensions = 0;
pf = (GstRegistryChunkPluginFeature *) tff;
/* save extensions */
if (factory->extensions) {
while (factory->extensions[tff->nextensions]) {
gst_registry_chunks_save_const_string (list,
factory->extensions[tff->nextensions++]);
}
}
/* save caps */
if (factory->caps) {
/* we copy the caps here so we can simplify them before saving. This
* is a lot faster when loading them later on */
GstCaps *copy = gst_caps_copy (factory->caps);
gst_caps_do_simplify (copy);
str = gst_caps_to_string (copy);
gst_caps_unref (copy);
gst_registry_chunks_save_string (list, str);
} else {
gst_registry_chunks_save_const_string (list, "");
}
} else if (GST_IS_INDEX_FACTORY (feature)) {
GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
pf = g_malloc0 (sizeof (GstRegistryChunkPluginFeature));
chk =
gst_registry_chunks_make_data (pf,
sizeof (GstRegistryChunkPluginFeature));
pf->rank = feature->rank;
/* pack element factory strings */
gst_registry_chunks_save_const_string (list, factory->longdesc);
} else {
GST_WARNING ("unhandled feature type '%s'", type_name);
}
if (pf) {
pf->rank = feature->rank;
*list = g_list_prepend (*list, chk);
/* pack plugin feature strings */
gst_registry_chunks_save_const_string (list, feature->name);
gst_registry_chunks_save_const_string (list, (gchar *) type_name);
return TRUE;
}
/* Errors */
fail:
g_free (chk);
g_free (pf);
return FALSE;
}
static gboolean
gst_registry_chunks_save_plugin_dep (GList ** list, GstPluginDep * dep)
{
GstRegistryChunkDep *ed;
GstRegistryChunk *chk;
gchar **s;
ed = g_new0 (GstRegistryChunkDep, 1);
chk = gst_registry_chunks_make_data (ed, sizeof (GstRegistryChunkDep));
ed->flags = dep->flags;
ed->n_env_vars = 0;
ed->n_paths = 0;
ed->n_names = 0;
ed->env_hash = dep->env_hash;
ed->stat_hash = dep->stat_hash;
for (s = dep->env_vars; s != NULL && *s != NULL; ++s, ++ed->n_env_vars)
gst_registry_chunks_save_string (list, g_strdup (*s));
for (s = dep->paths; s != NULL && *s != NULL; ++s, ++ed->n_paths)
gst_registry_chunks_save_string (list, g_strdup (*s));
for (s = dep->names; s != NULL && *s != NULL; ++s, ++ed->n_names)
gst_registry_chunks_save_string (list, g_strdup (*s));
*list = g_list_prepend (*list, chk);
GST_LOG ("Saved external plugin dependency");
return TRUE;
}
/*
* _priv_gst_registry_chunks_save_plugin:
*
* Adapt a GstPlugin to our GstRegistryChunkPluginElement structure, and
* prepend it as a GstRegistryChunk in the provided list.
*
*/
gboolean
_priv_gst_registry_chunks_save_plugin (GList ** list, GstRegistry * registry,
GstPlugin * plugin)
{
GstRegistryChunkPluginElement *pe;
GstRegistryChunk *chk;
GList *plugin_features = NULL;
GList *walk;
pe = g_malloc0 (sizeof (GstRegistryChunkPluginElement));
chk =
gst_registry_chunks_make_data (pe,
sizeof (GstRegistryChunkPluginElement));
pe->file_size = plugin->file_size;
pe->file_mtime = plugin->file_mtime;
pe->nfeatures = 0;
pe->n_deps = 0;
/* pack external deps */
for (walk = plugin->priv->deps; walk != NULL; walk = walk->next) {
if (!gst_registry_chunks_save_plugin_dep (list, walk->data)) {
GST_ERROR ("Could not save external plugin dependency, aborting.");
goto fail;
}
++pe->n_deps;
}
/* pack plugin features */
plugin_features =
gst_registry_get_feature_list_by_plugin (registry, plugin->desc.name);
for (walk = plugin_features; walk; walk = g_list_next (walk), pe->nfeatures++) {
GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
if (!gst_registry_chunks_save_feature (list, feature)) {
GST_ERROR ("Can't fill plugin feature, aborting.");
goto fail;
}
}
gst_plugin_feature_list_free (plugin_features);
/* pack cache data */
if (plugin->priv->cache_data) {
gchar *cache_str = gst_structure_to_string (plugin->priv->cache_data);
gst_registry_chunks_save_string (list, cache_str);
} else {
gst_registry_chunks_save_const_string (list, "");
}
/* pack plugin element strings */
gst_registry_chunks_save_const_string (list, plugin->desc.origin);
gst_registry_chunks_save_const_string (list, plugin->desc.package);
gst_registry_chunks_save_const_string (list, plugin->desc.source);
gst_registry_chunks_save_const_string (list, plugin->desc.license);
gst_registry_chunks_save_const_string (list, plugin->desc.version);
gst_registry_chunks_save_const_string (list, plugin->filename);
gst_registry_chunks_save_const_string (list, plugin->desc.description);
gst_registry_chunks_save_const_string (list, plugin->desc.name);
*list = g_list_prepend (*list, chk);
GST_DEBUG ("Found %d features in plugin \"%s\"", pe->nfeatures,
plugin->desc.name);
return TRUE;
/* Errors */
fail:
gst_plugin_feature_list_free (plugin_features);
g_free (chk);
g_free (pe);
return FALSE;
}
/*
* gst_registry_chunks_load_pad_template:
*
* Make a new GstStaticPadTemplate from current GstRegistryChunkPadTemplate
* structure.
*
* Returns: new GstStaticPadTemplate
*/
static gboolean
gst_registry_chunks_load_pad_template (GstElementFactory * factory, gchar ** in,
gchar * end)
{
GstRegistryChunkPadTemplate *pt;
GstStaticPadTemplate *template = NULL;
align (*in);
GST_DEBUG ("Reading/casting for GstRegistryChunkPadTemplate at address %p",
*in);
unpack_element (*in, pt, GstRegistryChunkPadTemplate, end, fail);
template = g_new0 (GstStaticPadTemplate, 1);
template->presence = pt->presence;
template->direction = pt->direction;
/* unpack pad template strings */
unpack_const_string (*in, template->name_template, end, fail);
unpack_string (*in, template->static_caps.string, end, fail);
__gst_element_factory_add_static_pad_template (factory, template);
GST_DEBUG ("Added pad_template %s", template->name_template);
return TRUE;
fail:
GST_INFO ("Reading pad template failed");
g_free (template);
return FALSE;
}
/*
* gst_registry_chunks_load_feature:
*
* Make a new GstPluginFeature from current binary plugin feature structure
*
* Returns: new GstPluginFeature
*/
static gboolean
gst_registry_chunks_load_feature (GstRegistry * registry, gchar ** in,
gchar * end, const gchar * plugin_name)
{
GstRegistryChunkPluginFeature *pf = NULL;
GstPluginFeature *feature = NULL;
gchar *type_name = NULL, *str;
GType type;
guint i;
/* unpack plugin feature strings */
unpack_string (*in, type_name, end, fail);
if (G_UNLIKELY (!type_name)) {
GST_ERROR ("No feature type name");
return FALSE;
}
GST_DEBUG ("Plugin '%s' feature typename : '%s'", plugin_name, type_name);
if (G_UNLIKELY (!(type = g_type_from_name (type_name)))) {
GST_ERROR ("Unknown type from typename '%s' for plugin '%s'", type_name,
plugin_name);
g_free (type_name);
return FALSE;
}
if (G_UNLIKELY ((feature = g_object_new (type, NULL)) == NULL)) {
GST_ERROR ("Can't create feature from type");
g_free (type_name);
return FALSE;
}
if (G_UNLIKELY (!GST_IS_PLUGIN_FEATURE (feature))) {
GST_ERROR ("typename : '%s' is not a plugin feature", type_name);
goto fail;
}
/* unpack more plugin feature strings */
unpack_string (*in, feature->name, end, fail);
if (GST_IS_ELEMENT_FACTORY (feature)) {
GstRegistryChunkElementFactory *ef;
guint n;
GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (feature);
align (*in);
GST_LOG ("Reading/casting for GstRegistryChunkElementFactory at address %p",
*in);
unpack_element (*in, ef, GstRegistryChunkElementFactory, end, fail);
pf = (GstRegistryChunkPluginFeature *) ef;
/* unpack element factory strings */
unpack_string (*in, factory->details.longname, end, fail);
unpack_string (*in, factory->details.klass, end, fail);
unpack_string (*in, factory->details.description, end, fail);
unpack_string (*in, factory->details.author, end, fail);
n = ef->npadtemplates;
GST_DEBUG ("Element factory : '%s' with npadtemplates=%d",
factory->details.longname, n);
/* load pad templates */
for (i = 0; i < n; i++) {
if (G_UNLIKELY (!gst_registry_chunks_load_pad_template (factory, in,
end))) {
GST_ERROR ("Error while loading binary pad template");
goto fail;
}
}
/* load uritypes */
if (G_UNLIKELY ((n = ef->nuriprotocols))) {
GST_DEBUG ("Reading %d UriTypes at address %p", n, *in);
align (*in);
factory->uri_type = *((guint *) * in);
*in += sizeof (factory->uri_type);
/*unpack_element(*in, &factory->uri_type, factory->uri_type, end, fail); */
factory->uri_protocols = g_new0 (gchar *, n + 1);
for (i = 0; i < n; i++) {
unpack_string (*in, str, end, fail);
factory->uri_protocols[i] = str;
}
}
/* load interfaces */
if (G_UNLIKELY ((n = ef->ninterfaces))) {
GST_DEBUG ("Reading %d Interfaces at address %p", n, *in);
for (i = 0; i < n; i++) {
unpack_string (*in, str, end, fail);
__gst_element_factory_add_interface (factory, str);
g_free (str);
}
}
} else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
GstRegistryChunkTypeFindFactory *tff;
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
align (*in);
GST_DEBUG
("Reading/casting for GstRegistryChunkPluginFeature at address %p",
*in);
unpack_element (*in, tff, GstRegistryChunkTypeFindFactory, end, fail);
pf = (GstRegistryChunkPluginFeature *) tff;
/* load caps */
unpack_string (*in, str, end, fail);
factory->caps = (str && *str) ? gst_caps_from_string (str) : NULL;
g_free (str);
/* load extensions */
if (tff->nextensions) {
GST_DEBUG ("Reading %d Typefind extensions at address %p",
tff->nextensions, *in);
factory->extensions = g_new0 (gchar *, tff->nextensions + 1);
for (i = 0; i < tff->nextensions; i++) {
unpack_string (*in, str, end, fail);
factory->extensions[i] = str;
}
}
} else if (GST_IS_INDEX_FACTORY (feature)) {
GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
align (*in);
GST_DEBUG
("Reading/casting for GstRegistryChunkPluginFeature at address %p",
*in);
unpack_element (*in, pf, GstRegistryChunkPluginFeature, end, fail);
/* unpack index factory strings */
unpack_string (*in, factory->longdesc, end, fail);
} else {
GST_WARNING ("unhandled factory type : %s", G_OBJECT_TYPE_NAME (feature));
goto fail;
}
feature->rank = pf->rank;
/* should already be the interned string, but better make sure */
feature->plugin_name = g_intern_string (plugin_name);
gst_registry_add_feature (registry, feature);
GST_DEBUG ("Added feature %s", feature->name);
g_free (type_name);
return TRUE;
/* Errors */
fail:
GST_INFO ("Reading plugin feature failed");
g_free (type_name);
if (feature) {
if (GST_IS_OBJECT (feature))
gst_object_unref (feature);
else
g_object_unref (feature);
}
return FALSE;
}
static gchar **
gst_registry_chunks_load_plugin_dep_strv (gchar ** in, gchar * end, guint n)
{
gchar **arr;
if (n == 0)
return NULL;
arr = g_new0 (gchar *, n + 1);
while (n > 0) {
unpack_string (*in, arr[n - 1], end, fail);
--n;
}
return arr;
fail:
GST_INFO ("Reading plugin dependency strings failed");
return NULL;
}
static gboolean
gst_registry_chunks_load_plugin_dep (GstPlugin * plugin, gchar ** in,
gchar * end)
{
GstPluginDep *dep;
GstRegistryChunkDep *d;
gchar **s;
align (*in);
GST_LOG_OBJECT (plugin, "Unpacking GstRegistryChunkDep from %p", *in);
unpack_element (*in, d, GstRegistryChunkDep, end, fail);
dep = g_new0 (GstPluginDep, 1);
dep->env_hash = d->env_hash;
dep->stat_hash = d->stat_hash;
dep->flags = d->flags;
dep->names = gst_registry_chunks_load_plugin_dep_strv (in, end, d->n_names);
dep->paths = gst_registry_chunks_load_plugin_dep_strv (in, end, d->n_paths);
dep->env_vars =
gst_registry_chunks_load_plugin_dep_strv (in, end, d->n_env_vars);
plugin->priv->deps = g_list_append (plugin->priv->deps, dep);
GST_DEBUG_OBJECT (plugin, "Loaded external plugin dependency from registry: "
"env_hash: %08x, stat_hash: %08x", dep->env_hash, dep->stat_hash);
for (s = dep->env_vars; s != NULL && *s != NULL; ++s)
GST_LOG_OBJECT (plugin, " evar: %s", *s);
for (s = dep->paths; s != NULL && *s != NULL; ++s)
GST_LOG_OBJECT (plugin, " path: %s", *s);
for (s = dep->names; s != NULL && *s != NULL; ++s)
GST_LOG_OBJECT (plugin, " name: %s", *s);
return TRUE;
fail:
GST_INFO ("Reading plugin dependency failed");
return FALSE;
}
/*
* _priv_gst_registry_chunks_load_plugin:
*
* Make a new GstPlugin from current GstRegistryChunkPluginElement structure
* and add it to the GstRegistry. Return an offset to the next
* GstRegistryChunkPluginElement structure.
*/
gboolean
_priv_gst_registry_chunks_load_plugin (GstRegistry * registry, gchar ** in,
gchar * end, GstPlugin ** out_plugin)
{
GstRegistryChunkPluginElement *pe;
GstPlugin *plugin = NULL;
gchar *cache_str = NULL;
guint i, n;
align (*in);
GST_LOG ("Reading/casting for GstRegistryChunkPluginElement at address %p",
*in);
unpack_element (*in, pe, GstRegistryChunkPluginElement, end, fail);
plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
/* TODO: also set GST_PLUGIN_FLAG_CONST */
plugin->flags |= GST_PLUGIN_FLAG_CACHED;
plugin->file_mtime = pe->file_mtime;
plugin->file_size = pe->file_size;
/* unpack plugin element strings */
unpack_const_string (*in, plugin->desc.name, end, fail);
unpack_string (*in, plugin->desc.description, end, fail);
unpack_string (*in, plugin->filename, end, fail);
unpack_const_string (*in, plugin->desc.version, end, fail);
unpack_const_string (*in, plugin->desc.license, end, fail);
unpack_const_string (*in, plugin->desc.source, end, fail);
unpack_const_string (*in, plugin->desc.package, end, fail);
unpack_const_string (*in, plugin->desc.origin, end, fail);
GST_LOG ("read strings for name='%s'", plugin->desc.name);
GST_LOG (" desc.description='%s'", plugin->desc.description);
GST_LOG (" filename='%s'", plugin->filename);
GST_LOG (" desc.version='%s'", plugin->desc.version);
GST_LOG (" desc.license='%s'", plugin->desc.license);
GST_LOG (" desc.source='%s'", plugin->desc.source);
GST_LOG (" desc.package='%s'", plugin->desc.package);
GST_LOG (" desc.origin='%s'", plugin->desc.origin);
/* unpack cache data */
unpack_string (*in, cache_str, end, fail);
if (*cache_str) {
plugin->priv->cache_data = gst_structure_from_string (cache_str, NULL);
}
g_free (cache_str);
plugin->basename = g_path_get_basename (plugin->filename);
/* Takes ownership of plugin */
gst_registry_add_plugin (registry, plugin);
n = pe->nfeatures;
GST_DEBUG ("Added plugin '%s' plugin with %d features from binary registry",
plugin->desc.name, n);
/* Load plugin features */
for (i = 0; i < n; i++) {
if (G_UNLIKELY (!gst_registry_chunks_load_feature (registry, in, end,
plugin->desc.name))) {
GST_ERROR ("Error while loading binary feature");
gst_registry_remove_plugin (registry, plugin);
goto fail;
}
}
/* Load external plugin dependencies */
for (i = 0; i < pe->n_deps; ++i) {
if (G_UNLIKELY (!gst_registry_chunks_load_plugin_dep (plugin, in, end))) {
GST_ERROR_OBJECT (plugin, "Could not read external plugin dependency");
gst_registry_remove_plugin (registry, plugin);
goto fail;
}
}
if (out_plugin)
*out_plugin = plugin;
return TRUE;
/* Errors */
fail:
GST_INFO ("Reading plugin failed");
return FALSE;
}

150
gst/gstregistrychunks.h Normal file
View file

@ -0,0 +1,150 @@
/* GStreamer
* Copyright (C) 2006 Josep Torra <josep@fluendo.com>
* Copyright (C) 2006 Mathieu Garcia <matthieu@fluendo.com>
* Copyright (C) 2006 Stefan Kost <ensonic@sonicpulse.de>
*
* gstregistrybinary.h: Header for registry handling
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_REGISTRYCHUNKS_H__
#define __GST_REGISTRYCHUNKS_H__
#include <gst/gstpad.h>
#include <gst/gstregistry.h>
/*
* we reference strings directly from the plugins and in this case set CONST to
* avoid freeing them
*/
enum {
GST_REGISTRY_CHUNK_FLAG_NONE = 0,
GST_REGISTRY_CHUNK_FLAG_CONST = 1
};
/*
* GstRegistryChunk:
*
* Header for binary blobs
*/
typedef struct _GstRegistryChunk
{
gpointer data;
guint size;
guint flags;
gboolean align;
} GstRegistryChunk;
/*
* GstRegistryChunkPluginElement:
*
* @n_deps: Says how many dependency structures follows.
*
* @nfeatures: says how many binary plugin feature structures we will have
* right after the structure itself.
*
* A structure containing (staticely) every information needed for a plugin
*/
typedef struct _GstRegistryChunkPluginElement
{
gulong file_size;
gulong file_mtime;
guint n_deps;
guint nfeatures;
} GstRegistryChunkPluginElement;
/* GstRegistryChunkDep:
*/
typedef struct _GstRegistryChunkDep
{
guint flags;
guint n_env_vars;
guint n_paths;
guint n_names;
guint env_hash;
guint stat_hash;
} GstRegistryChunkDep;
/*
* GstRegistryChunkPluginFeature:
* @rank: rank of the feature
*
* A structure containing the plugin features
*/
typedef struct _GstRegistryChunkPluginFeature
{
gulong rank;
} GstRegistryChunkPluginFeature;
/*
* GstRegistryChunkElementFactory:
* @npadtemplates: stores the number of GstRegistryChunkPadTemplate structures
* following the structure
* @ninterfaces: stores the number of interface names following the structure
* @nuriprotocols: stores the number of protocol strings following the structure
*
* A structure containing the element factory fields
*/
typedef struct _GstRegistryChunkElementFactory
{
GstRegistryChunkPluginFeature plugin_feature;
guint npadtemplates;
guint ninterfaces;
guint nuriprotocols;
} GstRegistryChunkElementFactory;
/*
* GstRegistryChunkTypeFindFactory:
* @nextensions: stores the number of typefind extensions
*
* A structure containing the element factory fields
*/
typedef struct _GstRegistryChunkTypeFindFactory
{
GstRegistryChunkPluginFeature plugin_feature;
guint nextensions;
} GstRegistryChunkTypeFindFactory;
/*
* GstRegistryChunkPadTemplate:
*
* A structure containing the static pad templates of a plugin feature
*/
typedef struct _GstRegistryChunkPadTemplate
{
guint direction; /* Either 0:"sink" or 1:"src" */
GstPadPresence presence;
} GstRegistryChunkPadTemplate;
G_BEGIN_DECLS
gboolean
_priv_gst_registry_chunks_save_plugin (GList ** list, GstRegistry * registry,
GstPlugin * plugin);
gboolean
_priv_gst_registry_chunks_load_plugin (GstRegistry * registry, gchar ** in,
gchar *end, GstPlugin **out_plugin);
G_END_DECLS
#endif /* __GST_REGISTRYCHUNKS_H__ */

View file

@ -18,7 +18,13 @@ endif
endif endif
endif endif
if GST_DISABLE_REGISTRY
SUBDIRS_HELPERS =
else
SUBDIRS_HELPERS = helpers
endif
SUBDIRS_ALWAYS = base controller dataprotocol SUBDIRS_ALWAYS = base controller dataprotocol
SUBDIRS = $(SUBDIRS_ALWAYS) $(SUBDIRS_CHECK) $(SUBDIRS_NET) SUBDIRS = $(SUBDIRS_ALWAYS) $(SUBDIRS_CHECK) $(SUBDIRS_NET) $(SUBDIRS_HELPERS)
DIST_SUBDIRS = $(SUBDIRS_ALWAYS) check net DIST_SUBDIRS = $(SUBDIRS_ALWAYS) check net helpers

2
libs/gst/helpers/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
plugin-scanner
*.o

View file

@ -0,0 +1,7 @@
# helpers_PROGRAMS = plugin-scanner
# FIXME: Subst helpersdir in configure.ac
noinst_PROGRAMS = plugin-scanner
plugin_scanner_SOURCES = plugin-scanner.c
plugin_scanner_CFLAGS = $(GST_OBJ_CFLAGS)
plugin_scanner_LDFLAGS = $(GST_OBJ_LIBS)

View file

@ -0,0 +1,67 @@
/* GStreamer
* Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
*
* plugin-scanner.c: tool to load plugins out of process for scanning
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Helper binary that does plugin-loading out of process and feeds results
* back to the parent over fds.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include <gst/gst_private.h>
int
main (int argc, char *argv[])
{
gboolean res;
char **my_argv;
int my_argc;
if (argc != 2 || strcmp (argv[1], "-l"))
return 1;
if (!g_thread_supported ())
g_thread_init (NULL);
my_argc = 2;
my_argv = g_malloc (my_argc * sizeof (char *));
my_argv[0] = argv[0];
my_argv[1] = "--gst-disable-registry-update";
#ifndef GST_DISABLE_REGISTRY
_gst_disable_registry_cache = TRUE;
#endif
res = gst_init_check (&my_argc, &my_argv, NULL);
g_free (my_argv);
if (!res)
return 1;
/* Create registry scanner listener and run */
if (!_gst_plugin_loader_client_run ())
return 1;
return 0;
}

View file

@ -34,6 +34,11 @@ plugin_name_cmp (GstPlugin * a, GstPlugin * b)
return strcmp (name_a, name_b); return strcmp (name_a, name_b);
} }
static gint
plugin_ptr_cmp (GstPlugin * a, GstPlugin * b)
{
return (a == b) ? 0 : 1;
}
static void static void
print_plugin (const gchar * marker, GstRegistry * registry, GstPlugin * plugin) print_plugin (const gchar * marker, GstRegistry * registry, GstPlugin * plugin)
@ -43,8 +48,9 @@ print_plugin (const gchar * marker, GstRegistry * registry, GstPlugin * plugin)
name = gst_plugin_get_name (plugin); name = gst_plugin_get_name (plugin);
GST_DEBUG ("%s: plugin %p %d %s", marker, plugin, GST_DEBUG ("%s: plugin %p %d %s file: %s", marker, plugin,
GST_OBJECT_REFCOUNT (plugin), name); GST_OBJECT_REFCOUNT (plugin), name,
GST_STR_NULL (gst_plugin_get_filename (plugin)));
features = gst_registry_get_feature_list_by_plugin (registry, name); features = gst_registry_get_feature_list_by_plugin (registry, name);
for (f = features; f != NULL; f = f->next) { for (f = features; f != NULL; f = f->next) {
@ -100,9 +106,9 @@ GST_START_TEST (test_registry_update)
GST_LOG (" -----------------------------------"); GST_LOG (" -----------------------------------");
/* static plugins should have the same refcount as before (ie. 2), whereas /* static plugins should have the same refcount as before (ie. 2), whereas
* file-based plugins should have been replaced by a newly-created objects * file-based plugins *may* have been replaced by a newly-created object
* (when reading the updated registry.xml file), so there should be only one * if the on-disk file changed (and was not yet loaded). There should be
* reference left for those, and that's ours */ * only one reference left for those, and that's ours */
for (l = plugins_before; l; l = l->next) { for (l = plugins_before; l; l = l->next) {
GstPlugin *plugin; GstPlugin *plugin;
@ -111,8 +117,8 @@ GST_START_TEST (test_registry_update)
print_plugin ("before2", registry, plugin); print_plugin ("before2", registry, plugin);
if (gst_plugin_get_filename (plugin)) { if (gst_plugin_get_filename (plugin)) {
/* file-based plugin */ /* file-based plugin. */
ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 1); ASSERT_OBJECT_REFCOUNT_BETWEEN (plugin, "plugin", 1, 2);
} else { } else {
/* static plugin */ /* static plugin */
ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 2); ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 2);
@ -123,20 +129,27 @@ GST_START_TEST (test_registry_update)
plugins_after = gst_registry_get_plugin_list (registry); plugins_after = gst_registry_get_plugin_list (registry);
for (l = plugins_after; l; l = l->next) { for (l = plugins_after; l; l = l->next) {
GstPlugin *plugin; GstPlugin *plugin = GST_PLUGIN (l->data);
plugin = GST_PLUGIN (l->data);
print_plugin ("after ", registry, plugin); print_plugin ("after ", registry, plugin);
/* file-based plugins should have a refcount of 2 (one for the registry, /* file-based plugins should have a refcount of 2 (one for the registry,
* one for us for the list), static plugins should have one of 3 (one for * one for us for the list) or 3 (one for the registry, one for the before
* the registry, one for the new list and one for the old list). * list, one for the after list), static plugins should have one of 3
* (one for the registry, one for the new list and one for the old list).
* This implicitly also makes sure that all static plugins are the same * This implicitly also makes sure that all static plugins are the same
* objects as they were before and that all non-static ones have been * objects as they were before. Non-static ones may or may not have been
* replaced by new objects */ * replaced by new objects */
if (gst_plugin_get_filename (plugin)) { if (gst_plugin_get_filename (plugin)) {
ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 2); if (g_list_find_custom (plugins_before, plugin,
(GCompareFunc) plugin_ptr_cmp) != NULL) {
/* Same plugin existed in the before list. Refcount must be 3 */
ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 3);
} else {
/* This plugin is newly created, so should only exist in the after list
* and the registry: Refcount must be 2 */
ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 2);
}
} else { } else {
ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 3); ASSERT_OBJECT_REFCOUNT (plugin, "plugin", 3);
} }
@ -165,15 +178,19 @@ GST_START_TEST (test_registry_update)
new_identity = gst_registry_lookup_feature (registry, "identity"); new_identity = gst_registry_lookup_feature (registry, "identity");
fail_unless (new_identity != NULL, "Can't find plugin feature 'identity'"); fail_unless (new_identity != NULL, "Can't find plugin feature 'identity'");
#if 0
fail_unless (old_identity != new_identity, "Old and new 'identity' feature " fail_unless (old_identity != new_identity, "Old and new 'identity' feature "
"should be different but are the same object"); "should be different but are the same object");
ASSERT_OBJECT_REFCOUNT (old_identity, "old identity feature after update", 1); ASSERT_OBJECT_REFCOUNT (old_identity, "old identity feature after update", 1);
#endif
new_pipeline = gst_registry_lookup_feature (registry, "pipeline"); new_pipeline = gst_registry_lookup_feature (registry, "pipeline");
fail_unless (new_pipeline != NULL, "Can't find plugin feature 'pipeline'"); fail_unless (new_pipeline != NULL, "Can't find plugin feature 'pipeline'");
#if 0
fail_unless (old_pipeline == new_pipeline, "Old and new 'pipeline' feature " fail_unless (old_pipeline == new_pipeline, "Old and new 'pipeline' feature "
"objects should be the same, but are different objects"); "objects should be the same, but are different objects");
#endif
gst_plugin_list_free (plugins_before); gst_plugin_list_free (plugins_before);
plugins_before = NULL; plugins_before = NULL;

View file

@ -42,8 +42,10 @@ EXPORTS
_gst_debug_get_category _gst_debug_get_category
_gst_debug_nameof_funcptr _gst_debug_nameof_funcptr
_gst_debug_register_funcptr _gst_debug_register_funcptr
_gst_disable_registry_cache DATA
_gst_element_error_printf _gst_element_error_printf
_gst_elementclass_factory DATA _gst_elementclass_factory DATA
_gst_plugin_loader_client_run
_gst_plugin_register_static _gst_plugin_register_static
_gst_trace_add_entry _gst_trace_add_entry
_gst_trace_mutex DATA _gst_trace_mutex DATA