gstreamer/gst/frei0r/gstfrei0r.c
Sebastian Dröge 8dc7347d65 frei0r: Fix scanning of plugin subdirectories and support different vendors
The frei0r spec specifies, that plugins can be in subdirectories inside
the main plugin directories to introduce new namespaces called vendors.
2010-09-25 10:46:34 +02:00

707 lines
21 KiB
C

/* GStreamer
* Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* 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 "gstfrei0r.h"
#include "gstfrei0rfilter.h"
#include "gstfrei0rsrc.h"
#include "gstfrei0rmixer.h"
#include <gst/controller/gstcontroller.h>
#include <string.h>
GST_DEBUG_CATEGORY (frei0r_debug);
#define GST_CAT_DEFAULT frei0r_debug
GstCaps *
gst_frei0r_caps_from_color_model (gint color_model)
{
switch (color_model) {
case F0R_COLOR_MODEL_BGRA8888:
return gst_caps_from_string (GST_VIDEO_CAPS_BGRA);
case F0R_COLOR_MODEL_RGBA8888:
return gst_caps_from_string (GST_VIDEO_CAPS_RGBA);
case F0R_COLOR_MODEL_PACKED32:
return gst_caps_from_string (GST_VIDEO_CAPS_BGRA " ; "
GST_VIDEO_CAPS_RGBA " ; "
GST_VIDEO_CAPS_ABGR " ; "
GST_VIDEO_CAPS_ARGB " ; "
GST_VIDEO_CAPS_BGRx " ; "
GST_VIDEO_CAPS_RGBx " ; "
GST_VIDEO_CAPS_xBGR " ; "
GST_VIDEO_CAPS_xRGB " ; " GST_VIDEO_CAPS_YUV ("AYUV"));
default:
break;
}
return NULL;
}
void
gst_frei0r_klass_install_properties (GObjectClass * gobject_class,
GstFrei0rFuncTable * ftable, GstFrei0rProperty * properties,
gint n_properties)
{
gint i, count = 1;
f0r_instance_t *instance = ftable->construct (640, 480);
g_assert (instance);
for (i = 0; i < n_properties; i++) {
f0r_param_info_t *param_info = &properties[i].info;
gchar *prop_name;
ftable->get_param_info (param_info, i);
prop_name = g_ascii_strdown (param_info->name, -1);
g_strcanon (prop_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
/* satisfy glib2 (argname[0] must be [A-Za-z]) */
if (!((prop_name[0] >= 'a' && prop_name[0] <= 'z') ||
(prop_name[0] >= 'A' && prop_name[0] <= 'Z'))) {
gchar *tempstr = prop_name;
prop_name = g_strconcat ("param-", prop_name, NULL);
g_free (tempstr);
}
properties[i].prop_id = count;
properties[i].prop_idx = i;
ftable->get_param_value (instance, &properties[i].default_value, i);
if (param_info->type == F0R_PARAM_STRING)
properties[i].default_value.data.s =
g_strdup (properties[i].default_value.data.s);
switch (param_info->type) {
case F0R_PARAM_BOOL:
g_object_class_install_property (gobject_class, count++,
g_param_spec_boolean (prop_name, param_info->name,
param_info->explanation, properties[i].default_value.data.b,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
properties[i].n_prop_ids = 1;
break;
case F0R_PARAM_DOUBLE:{
gdouble def = properties[i].default_value.data.d;
/* If the default is NAN, +-INF we use 0.0 */
if (!(def <= G_MAXDOUBLE && def >= -G_MAXDOUBLE))
def = 0.0;
g_object_class_install_property (gobject_class, count++,
g_param_spec_double (prop_name, param_info->name,
param_info->explanation, -G_MAXDOUBLE, G_MAXDOUBLE, def,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
properties[i].n_prop_ids = 1;
break;
}
case F0R_PARAM_STRING:
g_object_class_install_property (gobject_class, count++,
g_param_spec_string (prop_name, param_info->name,
param_info->explanation, properties[i].default_value.data.s,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
properties[i].n_prop_ids = 1;
break;
case F0R_PARAM_COLOR:{
gchar *prop_name_full;
gchar *prop_nick_full;
gdouble def;
def = properties[i].default_value.data.color.r;
/* If the default is out of range we use 0.0 */
if (!(def <= 1.0 && def >= 0.0))
def = 0.0;
prop_name_full = g_strconcat (prop_name, "-r", NULL);
prop_nick_full = g_strconcat (param_info->name, "-R", NULL);
g_object_class_install_property (gobject_class, count++,
g_param_spec_float (prop_name_full, prop_nick_full,
param_info->explanation, 0.0, 1.0, def,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_free (prop_name_full);
g_free (prop_nick_full);
def = properties[i].default_value.data.color.g;
/* If the default is out of range we use 0.0 */
if (!(def <= 1.0 && def >= 0.0))
def = 0.0;
prop_name_full = g_strconcat (prop_name, "-g", NULL);
prop_nick_full = g_strconcat (param_info->name, "-G", NULL);
g_object_class_install_property (gobject_class, count++,
g_param_spec_float (prop_name_full, param_info->name,
param_info->explanation, 0.0, 1.0, def,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_free (prop_name_full);
g_free (prop_nick_full);
def = properties[i].default_value.data.color.b;
/* If the default is out of range we use 0.0 */
if (!(def <= 1.0 && def >= 0.0))
def = 0.0;
prop_name_full = g_strconcat (prop_name, "-b", NULL);
prop_nick_full = g_strconcat (param_info->name, "-B", NULL);
g_object_class_install_property (gobject_class, count++,
g_param_spec_float (prop_name_full, param_info->name,
param_info->explanation, 0.0, 1.0, def,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_free (prop_name_full);
g_free (prop_nick_full);
properties[i].n_prop_ids = 3;
break;
}
case F0R_PARAM_POSITION:{
gchar *prop_name_full;
gchar *prop_nick_full;
gdouble def;
def = properties[i].default_value.data.position.x;
/* If the default is out of range we use 0.0 */
if (!(def <= 1.0 && def >= 0.0))
def = 0.0;
prop_name_full = g_strconcat (prop_name, "-x", NULL);
prop_nick_full = g_strconcat (param_info->name, "-X", NULL);
g_object_class_install_property (gobject_class, count++,
g_param_spec_double (prop_name_full, param_info->name,
param_info->explanation, 0.0, 1.0, def,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_free (prop_name_full);
g_free (prop_nick_full);
def = properties[i].default_value.data.position.y;
/* If the default is out of range we use 0.0 */
if (!(def <= 1.0 && def >= 0.0))
def = 0.0;
prop_name_full = g_strconcat (prop_name, "-Y", NULL);
prop_nick_full = g_strconcat (param_info->name, "-X", NULL);
g_object_class_install_property (gobject_class, count++,
g_param_spec_double (prop_name_full, param_info->name,
param_info->explanation, 0.0, 1.0, def,
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_free (prop_name_full);
g_free (prop_nick_full);
properties[i].n_prop_ids = 2;
break;
}
default:
g_assert_not_reached ();
break;
}
g_free (prop_name);
}
ftable->destruct (instance);
}
GstFrei0rPropertyValue *
gst_frei0r_property_cache_init (GstFrei0rProperty * properties,
gint n_properties)
{
gint i;
GstFrei0rPropertyValue *ret = g_new0 (GstFrei0rPropertyValue, n_properties);
for (i = 0; i < n_properties; i++) {
memcpy (&ret[i].data, &properties[i].default_value,
sizeof (GstFrei0rPropertyValue));
if (properties[i].info.type == F0R_PARAM_STRING)
ret[i].data.s = g_strdup (ret[i].data.s);
}
return ret;
}
void
gst_frei0r_property_cache_free (GstFrei0rProperty * properties,
GstFrei0rPropertyValue * property_cache, gint n_properties)
{
gint i;
for (i = 0; i < n_properties; i++) {
if (properties[i].info.type == F0R_PARAM_STRING)
g_free (property_cache[i].data.s);
}
g_free (property_cache);
}
f0r_instance_t *
gst_frei0r_instance_construct (GstFrei0rFuncTable * ftable,
GstFrei0rProperty * properties, gint n_properties,
GstFrei0rPropertyValue * property_cache, gint width, gint height)
{
f0r_instance_t *instance = ftable->construct (width, height);
gint i;
for (i = 0; i < n_properties; i++) {
if (properties[i].info.type == F0R_PARAM_STRING)
ftable->set_param_value (instance, property_cache[i].data.s, i);
else
ftable->set_param_value (instance, &property_cache[i].data, i);
}
return instance;
}
gboolean
gst_frei0r_get_property (f0r_instance_t * instance, GstFrei0rFuncTable * ftable,
GstFrei0rProperty * properties, gint n_properties,
GstFrei0rPropertyValue * property_cache, guint prop_id, GValue * value)
{
gint i;
GstFrei0rProperty *prop = NULL;
for (i = 0; i < n_properties; i++) {
if (properties[i].prop_id <= prop_id &&
properties[i].prop_id + properties[i].n_prop_ids > prop_id) {
prop = &properties[i];
break;
}
}
if (!prop)
return FALSE;
switch (prop->info.type) {
case F0R_PARAM_BOOL:{
gdouble d;
if (instance)
ftable->get_param_value (instance, &d, prop->prop_idx);
else
d = property_cache[prop->prop_idx].data.b;
g_value_set_boolean (value, (d < 0.5) ? FALSE : TRUE);
break;
}
case F0R_PARAM_DOUBLE:{
gdouble d;
if (instance)
ftable->get_param_value (instance, &d, prop->prop_idx);
else
d = property_cache[prop->prop_idx].data.d;
g_value_set_double (value, d);
break;
}
case F0R_PARAM_STRING:{
const gchar *s;
if (instance)
ftable->get_param_value (instance, &s, prop->prop_idx);
else
s = property_cache[prop->prop_idx].data.s;
g_value_set_string (value, s);
break;
}
case F0R_PARAM_COLOR:{
f0r_param_color_t color;
if (instance)
ftable->get_param_value (instance, &color, prop->prop_idx);
else
color = property_cache[prop->prop_idx].data.color;
switch (prop_id - prop->prop_id) {
case 0:
g_value_set_float (value, color.r);
break;
case 1:
g_value_set_float (value, color.g);
break;
case 2:
g_value_set_float (value, color.b);
break;
}
break;
}
case F0R_PARAM_POSITION:{
f0r_param_position_t position;
if (instance)
ftable->get_param_value (instance, &position, prop->prop_idx);
else
position = property_cache[prop->prop_idx].data.position;
switch (prop_id - prop->prop_id) {
case 0:
g_value_set_double (value, position.x);
break;
case 1:
g_value_set_double (value, position.y);
break;
}
break;
}
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
gboolean
gst_frei0r_set_property (f0r_instance_t * instance, GstFrei0rFuncTable * ftable,
GstFrei0rProperty * properties, gint n_properties,
GstFrei0rPropertyValue * property_cache, guint prop_id,
const GValue * value)
{
GstFrei0rProperty *prop = NULL;
gint i;
for (i = 0; i < n_properties; i++) {
if (properties[i].prop_id <= prop_id &&
properties[i].prop_id + properties[i].n_prop_ids > prop_id) {
prop = &properties[i];
break;
}
}
if (!prop)
return FALSE;
switch (prop->info.type) {
case F0R_PARAM_BOOL:{
gboolean b = g_value_get_boolean (value);
gdouble d = b ? 1.0 : 0.0;
if (instance)
ftable->set_param_value (instance, &d, prop->prop_idx);
property_cache[prop->prop_idx].data.b = d;
break;
}
case F0R_PARAM_DOUBLE:{
gdouble d = g_value_get_double (value);
if (instance)
ftable->set_param_value (instance, &d, prop->prop_idx);
property_cache[prop->prop_idx].data.d = d;
break;
}
case F0R_PARAM_STRING:{
gchar *s = g_value_dup_string (value);
/* Copies the string */
if (instance)
ftable->set_param_value (instance, s, prop->prop_idx);
property_cache[prop->prop_idx].data.s = s;
break;
}
case F0R_PARAM_COLOR:{
gfloat f = g_value_get_float (value);
f0r_param_color_t *color = &property_cache[prop->prop_idx].data.color;
switch (prop_id - prop->prop_id) {
case 0:
color->r = f;
break;
case 1:
color->g = f;
break;
case 2:
color->b = f;
break;
default:
g_assert_not_reached ();
}
if (instance)
ftable->set_param_value (instance, color, prop->prop_idx);
break;
}
case F0R_PARAM_POSITION:{
gdouble d = g_value_get_double (value);
f0r_param_position_t *position =
&property_cache[prop->prop_idx].data.position;
switch (prop_id - prop->prop_id) {
case 0:
position->x = d;
break;
case 1:
position->y = d;
break;
default:
g_assert_not_reached ();
}
if (instance)
ftable->set_param_value (instance, position, prop->prop_idx);
break;
}
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
static gboolean
register_plugin (GstPlugin * plugin, const gchar * vendor,
const gchar * filename)
{
GModule *module;
GstFrei0rPluginRegisterReturn ret = GST_FREI0R_PLUGIN_REGISTER_RETURN_FAILED;
GstFrei0rFuncTable ftable = { NULL, };
gint i;
f0r_plugin_info_t info = { NULL, };
f0r_instance_t *instance = NULL;
GST_DEBUG ("Registering plugin '%s'", filename);
module = g_module_open (filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
if (!module) {
GST_WARNING ("Failed to load plugin");
return FALSE;
}
if (!g_module_symbol (module, "f0r_init", (gpointer *) & ftable.init)) {
GST_INFO ("No frei0r plugin");
g_module_close (module);
return FALSE;
}
if (!g_module_symbol (module, "f0r_deinit", (gpointer *) & ftable.deinit) ||
!g_module_symbol (module, "f0r_construct",
(gpointer *) & ftable.construct)
|| !g_module_symbol (module, "f0r_destruct",
(gpointer *) & ftable.destruct)
|| !g_module_symbol (module, "f0r_get_plugin_info",
(gpointer *) & ftable.get_plugin_info)
|| !g_module_symbol (module, "f0r_get_param_info",
(gpointer *) & ftable.get_param_info)
|| !g_module_symbol (module, "f0r_set_param_value",
(gpointer *) & ftable.set_param_value)
|| !g_module_symbol (module, "f0r_get_param_value",
(gpointer *) & ftable.get_param_value))
goto invalid_frei0r_plugin;
/* One of these must exist */
g_module_symbol (module, "f0r_update", (gpointer *) & ftable.update);
g_module_symbol (module, "f0r_update2", (gpointer *) & ftable.update2);
if (!ftable.init ()) {
GST_WARNING ("Failed to initialize plugin");
g_module_close (module);
return FALSE;
}
if (!ftable.update && !ftable.update2)
goto invalid_frei0r_plugin;
ftable.get_plugin_info (&info);
if (info.frei0r_version > 1) {
GST_WARNING ("Unsupported frei0r version %d", info.frei0r_version);
ftable.deinit ();
g_module_close (module);
return FALSE;
}
if (info.color_model > F0R_COLOR_MODEL_PACKED32) {
GST_WARNING ("Unsupported color model %d", info.color_model);
ftable.deinit ();
g_module_close (module);
return FALSE;
}
for (i = 0; i < info.num_params; i++) {
f0r_param_info_t pinfo = { NULL, };
ftable.get_param_info (&pinfo, i);
if (pinfo.type > F0R_PARAM_STRING) {
GST_WARNING ("Unsupported parameter type %d", pinfo.type);
ftable.deinit ();
g_module_close (module);
return FALSE;
}
}
instance = ftable.construct (640, 480);
if (!instance) {
GST_WARNING ("Failed to instanciate plugin '%s'", info.name);
ftable.deinit ();
g_module_close (module);
return FALSE;
}
ftable.destruct (instance);
switch (info.plugin_type) {
case F0R_PLUGIN_TYPE_FILTER:
ret = gst_frei0r_filter_register (plugin, vendor, &info, &ftable);
break;
case F0R_PLUGIN_TYPE_SOURCE:
ret = gst_frei0r_src_register (plugin, vendor, &info, &ftable);
break;
case F0R_PLUGIN_TYPE_MIXER2:
case F0R_PLUGIN_TYPE_MIXER3:
ret = gst_frei0r_mixer_register (plugin, vendor, &info, &ftable);
break;
default:
break;
}
switch (ret) {
case GST_FREI0R_PLUGIN_REGISTER_RETURN_OK:
return TRUE;
case GST_FREI0R_PLUGIN_REGISTER_RETURN_FAILED:
GST_ERROR ("Failed to register frei0r plugin");
ftable.deinit ();
g_module_close (module);
return FALSE;
case GST_FREI0R_PLUGIN_REGISTER_RETURN_ALREADY_REGISTERED:
GST_DEBUG ("frei0r plugin already registered");
ftable.deinit ();
g_module_close (module);
return TRUE;
default:
g_return_val_if_reached (FALSE);
}
g_return_val_if_reached (FALSE);
invalid_frei0r_plugin:
GST_ERROR ("Invalid frei0r plugin");
ftable.deinit ();
g_module_close (module);
return FALSE;
}
static gboolean
register_plugins (GstPlugin * plugin, GHashTable * plugin_names,
const gchar * path, const gchar * base_path)
{
GDir *dir;
gchar *filename;
const gchar *entry_name;
gboolean ret = TRUE;
GST_DEBUG ("Scanning directory '%s' for frei0r plugins", path);
dir = g_dir_open (path, 0, NULL);
if (!dir)
return FALSE;
while ((entry_name = g_dir_read_name (dir))) {
gchar *tmp, *vendor = NULL;
gchar *hashtable_name;
tmp = g_strdup (path + strlen (base_path));
if (*tmp == G_DIR_SEPARATOR && *(tmp + 1))
vendor = tmp + 1;
else if (*tmp)
vendor = tmp;
if (vendor)
hashtable_name = g_strconcat (vendor, "-", entry_name, NULL);
else
hashtable_name = g_strdup (entry_name);
if (g_hash_table_lookup_extended (plugin_names, hashtable_name, NULL, NULL)) {
g_free (hashtable_name);
continue;
}
filename = g_build_filename (path, entry_name, NULL);
if ((g_str_has_suffix (filename, G_MODULE_SUFFIX)
#ifdef GST_EXTRA_MODULE_SUFFIX
|| g_str_has_suffix (filename, GST_EXTRA_MODULE_SUFFIX)
#endif
) && g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
gboolean this_ret;
this_ret = register_plugin (plugin, vendor, filename);
if (this_ret)
g_hash_table_insert (plugin_names, g_strdup (hashtable_name), NULL);
ret = ret && this_ret;
} else if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
ret = ret && register_plugins (plugin, plugin_names, filename, base_path);
}
g_free (filename);
g_free (hashtable_name);
g_free (tmp);
}
g_dir_close (dir);
return ret;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
const gchar *homedir;
gchar *path;
GHashTable *plugin_names;
const gchar *frei0r_path;
GST_DEBUG_CATEGORY_INIT (frei0r_debug, "frei0r", 0, "frei0r");
gst_controller_init (NULL, NULL);
gst_plugin_add_dependency_simple (plugin,
"FREI0R_PATH:HOME/.frei0r-1/lib",
"/usr/lib/frei0r-1:/usr/local/lib/frei0r-1:"
"/usr/lib32/frei0r-1:/usr/local/lib32/frei0r-1:"
"/usr/lib64/frei0r-1:/usr/local/lib64/frei0r-1",
NULL, GST_PLUGIN_DEPENDENCY_FLAG_RECURSE);
plugin_names =
g_hash_table_new_full ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal,
(GDestroyNotify) g_free, NULL);
frei0r_path = g_getenv ("FREI0R_PATH");
if (frei0r_path && *frei0r_path) {
gchar **p, **paths = g_strsplit (frei0r_path, ":", -1);
for (p = paths; *p; p++) {
register_plugins (plugin, plugin_names, *p, *p);
}
g_strfreev (paths);
} else {
#define register_plugins2(plugin, pn, p) register_plugins(plugin, pn, p, p)
homedir = g_get_home_dir ();
path = g_build_filename (homedir, ".frei0r-1", "lib", NULL);
register_plugins2 (plugin, plugin_names, path);
g_free (path);
register_plugins2 (plugin, plugin_names, "/usr/local/lib/frei0r-1");
register_plugins2 (plugin, plugin_names, "/usr/lib/frei0r-1");
register_plugins2 (plugin, plugin_names, "/usr/local/lib32/frei0r-1");
register_plugins2 (plugin, plugin_names, "/usr/lib32/frei0r-1");
register_plugins2 (plugin, plugin_names, "/usr/local/lib64/frei0r-1");
register_plugins2 (plugin, plugin_names, "/usr/lib64/frei0r-1");
#undef register_plugins2
}
g_hash_table_unref (plugin_names);
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"frei0r",
"frei0r plugin library",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)